diff --git a/AGENTS.md b/AGENTS.md index bca576c..5ff4806 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,10 +43,6 @@ Do not place implementation code outside the matching root. Shared contracts mus Any task that touches CSS must keep CSS declarations compressed and compact. Do not expand a single selector's style block across many lines when it can be written as a concise one-line rule. -## Development Workflow - -- Do not use OpenSpec artifacts, commands, or skills for new work. Implement requested changes directly in the appropriate project root and record the relevant verification evidence in the handoff. - ## Task Creation Rules When creating a task, include the following prompt boundaries before implementation starts: diff --git a/openspec/changes/add-local-docker-deployment/design.md b/openspec/changes/add-local-docker-deployment/design.md deleted file mode 100644 index 93d17eb..0000000 --- a/openspec/changes/add-local-docker-deployment/design.md +++ /dev/null @@ -1,32 +0,0 @@ -# Design - -## Deployment shape - -The local Docker deployment runs three services: - -- `platform`: Go backend listening on `:8080`, using file-backed storage under `/data/platform`. -- `run`: Go worker in `worker` mode, connecting to `http://platform:8080`, using `/data/run/workspace` and `/data/run/spool`. -- `platform_web`: static Vite build served by Nginx, proxying `/api/v1` and `/healthz` to `platform:8080`. - -This matches the current architecture without introducing a new database adapter. The platform metadata and segmented log bodies are persisted in a Docker named volume. The worker has its own named volume for local workspace and spool data. - -## Local debugging - -Direct local execution remains environment-variable based: - -- Root `.env.example` documents the common three-process setup. -- `platform/.env.example` documents backend storage knobs. -- `run/.env.example` documents worker identity, platform URL, workspace, and scheduling knobs. -- `platform_web/.env.example` documents Vite browser API and dev proxy knobs. - -Developers can copy the example files to `.env` or source/export the variables before running `go run` / `npm run dev`. The project intentionally keeps Go config loading simple and does not require a checked-in secret-bearing config file. - -## Storage guidance - -The Docker compose default uses the durable file backend because it works without external services: - -- `PLATFORM_METADATA_PATH=/data/platform/metadata.json` -- `PLATFORM_LOG_DIR=/data/platform/logs` - -For larger production deployments, MySQL/Postgres should be introduced as a metadata repository adapter in a future OpenSpec change. High-volume log bodies should use a log-optimized backend such as ClickHouse, Loki, OpenSearch/Elasticsearch, or object-storage segments behind `LogBodyStore`; Docker compose does not pretend that MySQL is an adequate row-per-log-line body store. - diff --git a/openspec/changes/add-local-docker-deployment/proposal.md b/openspec/changes/add-local-docker-deployment/proposal.md deleted file mode 100644 index 65d12ab..0000000 --- a/openspec/changes/add-local-docker-deployment/proposal.md +++ /dev/null @@ -1,19 +0,0 @@ -# Add local Docker deployment - -## Why - -Operators need a repeatable way to run the platform backend, run worker, and platform web console with persistent local data. Local debugging also needs clear environment files so storage paths, run identity, platform API proxying, and browser-facing API URLs are easy to modify without changing code. - -## What Changes - -- Add Dockerfiles for `platform/`, `run/`, and `platform_web/`. -- Add a root `docker-compose.yml` that starts platform, run worker, and web console with named volumes for platform data and run workspace/spool data. -- Add local `.env.example` files documenting the runtime variables for Docker and direct local execution. -- Update README files to explain Docker deployment, local direct execution, and which config files to edit. - -## Impact - -- Adds local deployment configuration only; it does not add billing, cloud host sales, external provider workflows, or production log database adapters. -- Keeps platform data durable through mounted volumes and the existing file storage backend. -- Keeps frontend/browser access platform-mediated through `/api/v1` proxying. - diff --git a/openspec/changes/add-local-docker-deployment/specs/local-docker-deployment/spec.md b/openspec/changes/add-local-docker-deployment/specs/local-docker-deployment/spec.md deleted file mode 100644 index 69115cc..0000000 --- a/openspec/changes/add-local-docker-deployment/specs/local-docker-deployment/spec.md +++ /dev/null @@ -1,37 +0,0 @@ -# local-docker-deployment Specification - -## ADDED Requirements - -### Requirement: Docker deployment files - -The repository SHALL provide a local Docker deployment that starts the platform backend, run worker, and platform web console. - -#### Scenario: Compose starts local services with persistent directories - -- **GIVEN** an operator runs the local compose file -- **WHEN** the services start -- **THEN** the platform SHALL use a durable mounted data directory -- **AND** the run worker SHALL use a mounted workspace/spool directory -- **AND** the web console SHALL proxy platform API calls through platform-owned routes. - -### Requirement: Local debugging configuration examples - -The repository SHALL document direct local execution configuration through example environment files. - -#### Scenario: Developer wants to change ports or storage paths - -- **GIVEN** a developer wants to run the platform, run worker, and web console directly -- **WHEN** they inspect the environment examples -- **THEN** they SHALL find the platform listen address, storage backend, metadata path, log directory, run platform URL, run workspace/spool roots, and web API/proxy settings. - -### Requirement: Storage guidance remains scoped - -The deployment documentation SHALL distinguish metadata storage from log body storage. - -#### Scenario: Operator asks where large server logs should go - -- **GIVEN** a deployment with hundreds or thousands of servers -- **WHEN** the operator reads the deployment guidance -- **THEN** it SHALL say relational databases are for metadata and indexes -- **AND** it SHALL recommend log-optimized backends for high-volume log bodies in future adapters. - diff --git a/openspec/changes/add-local-docker-deployment/tasks.md b/openspec/changes/add-local-docker-deployment/tasks.md deleted file mode 100644 index 94e001c..0000000 --- a/openspec/changes/add-local-docker-deployment/tasks.md +++ /dev/null @@ -1,22 +0,0 @@ -## 1. OpenSpec Artifacts - -- [x] 1.1 Create proposal, design, spec, and tasks for local Docker deployment and debugging configuration. -- [x] 1.2 Validate the change with `openspec validate add-local-docker-deployment --strict`. - -## 2. Docker Deployment - -- [x] 2.1 Add Dockerfiles for platform, run, and platform_web. -- [x] 2.2 Add a root compose file with persistent volumes and safe service wiring. -- [x] 2.3 Add Docker ignore rules to keep builds small and avoid copying local data. - -## 3. Local Debug Configuration - -- [x] 3.1 Add example environment files for root orchestration, platform, run, and platform_web. -- [x] 3.2 Document which variables to modify for Docker and direct local execution. - -## 4. Verification - -- [x] 4.1 Validate compose syntax with `docker compose config`. -- [x] 4.2 Run focused backend tests for platform and run config packages. -- [x] 4.3 Run `scripts/check-structure.sh`. -- [x] 4.4 Run `openspec validate add-local-docker-deployment --strict`. diff --git a/openspec/changes/add-mysql-platform-metadata-storage/design.md b/openspec/changes/add-mysql-platform-metadata-storage/design.md deleted file mode 100644 index 25c300a..0000000 --- a/openspec/changes/add-mysql-platform-metadata-storage/design.md +++ /dev/null @@ -1,36 +0,0 @@ -# Design - -## Metadata backend - -`PLATFORM_STORAGE_BACKEND` selects the platform metadata store: - -- `file`: default local durable snapshot at `PLATFORM_METADATA_PATH`. -- `memory`: test/disposable storage. -- `mysql`: MySQL-backed metadata snapshot using `PLATFORM_MYSQL_DSN`. - -The first MySQL implementation stores one platform-owned JSON snapshot in a `platform_metadata_snapshots` table. This gives operators a real durable MySQL option now while preserving the existing `repo.Store` boundary. Later changes can normalize individual repositories into relational tables without changing handlers or services. - -## Log body backend - -`PLATFORM_LOG_BODY_BACKEND` selects log body storage separately: - -- empty: follows the metadata backend, except `mysql` maps to `file`. -- `file`: segmented JSONL log files in `PLATFORM_LOG_DIR`. -- `memory`: tests/disposable local runs. - -MySQL metadata storage does not imply MySQL log bodies. Hundreds or thousands of servers should use segmented files for local deployments and log-optimized stores such as ClickHouse, Loki, OpenSearch/Elasticsearch, or object-storage segments in production. - -## Docker guidance - -The root `docker-compose.yml` keeps the default file backend. It includes commented MySQL service/config blocks so operators can uncomment them when they want local MySQL metadata: - -```text -PLATFORM_STORAGE_BACKEND=mysql -PLATFORM_MYSQL_DSN=platform:platform@tcp(mysql:3306)/platform?parseTime=true -PLATFORM_LOG_BODY_BACKEND=file -``` - -## Failure behavior - -If `PLATFORM_STORAGE_BACKEND=mysql` is set without `PLATFORM_MYSQL_DSN`, platform startup fails with a direct configuration error. If the DSN is present but the database is unreachable, startup fails fast rather than silently falling back to memory. - diff --git a/openspec/changes/add-mysql-platform-metadata-storage/proposal.md b/openspec/changes/add-mysql-platform-metadata-storage/proposal.md deleted file mode 100644 index cca6096..0000000 --- a/openspec/changes/add-mysql-platform-metadata-storage/proposal.md +++ /dev/null @@ -1,20 +0,0 @@ -# Add MySQL platform metadata storage - -## Why - -The platform now has durable file storage, but Docker/local configuration does not expose a real MySQL option. Operators need a clear `PLATFORM_STORAGE_BACKEND=mysql` path with commented configuration examples. They also need the deployment docs to make the log storage boundary explicit: MySQL is for platform metadata, not high-volume row-per-log-line bodies. - -## What Changes - -- Add MySQL metadata storage configuration through `PLATFORM_MYSQL_DSN`. -- Add a MySQL-backed `repo.Store` implementation that persists platform metadata snapshots in a platform-owned table. -- Separate metadata backend selection from log body backend selection with `PLATFORM_LOG_BODY_BACKEND`. -- Update Docker compose/env examples with commented MySQL configuration. -- Document how to configure MySQL locally and in Docker, and clarify that log bodies remain on `LogBodyStore`. - -## Impact - -- Operators can configure platform metadata persistence with MySQL without changing code. -- Existing file-backed storage remains the default. -- Logs continue to use file segments by default; production log analytics backends remain a future adapter behind `LogBodyStore`. - diff --git a/openspec/changes/add-mysql-platform-metadata-storage/specs/mysql-platform-metadata-storage/spec.md b/openspec/changes/add-mysql-platform-metadata-storage/specs/mysql-platform-metadata-storage/spec.md deleted file mode 100644 index 6397cd7..0000000 --- a/openspec/changes/add-mysql-platform-metadata-storage/specs/mysql-platform-metadata-storage/spec.md +++ /dev/null @@ -1,45 +0,0 @@ -# mysql-platform-metadata-storage Specification - -## ADDED Requirements - -### Requirement: MySQL metadata backend configuration - -The platform SHALL support `PLATFORM_STORAGE_BACKEND=mysql` for metadata persistence. - -#### Scenario: MySQL backend is configured with a DSN - -- **GIVEN** `PLATFORM_STORAGE_BACKEND=mysql` -- **AND** `PLATFORM_MYSQL_DSN` points to a reachable database -- **WHEN** the platform starts -- **THEN** it SHALL initialize a MySQL metadata store -- **AND** it SHALL create required metadata storage structures when missing. - -#### Scenario: MySQL backend is missing a DSN - -- **GIVEN** `PLATFORM_STORAGE_BACKEND=mysql` -- **AND** `PLATFORM_MYSQL_DSN` is empty -- **WHEN** the platform starts -- **THEN** startup SHALL fail with a clear configuration error. - -### Requirement: Log body backend remains separate - -The platform SHALL configure log body storage separately from metadata storage. - -#### Scenario: MySQL metadata uses file log bodies by default - -- **GIVEN** `PLATFORM_STORAGE_BACKEND=mysql` -- **AND** `PLATFORM_LOG_BODY_BACKEND` is empty -- **WHEN** the platform starts -- **THEN** log bodies SHALL use the file segmented backend -- **AND** log entries SHALL NOT be stored as row-per-line MySQL metadata. - -### Requirement: MySQL configuration is documented - -The repository SHALL include commented MySQL examples in local env and Docker configuration docs. - -#### Scenario: Operator wants to configure MySQL - -- **GIVEN** an operator reads the env examples or README -- **WHEN** they search for MySQL configuration -- **THEN** they SHALL find `PLATFORM_STORAGE_BACKEND=mysql`, `PLATFORM_MYSQL_DSN`, and `PLATFORM_LOG_BODY_BACKEND=file` examples. - diff --git a/openspec/changes/add-mysql-platform-metadata-storage/tasks.md b/openspec/changes/add-mysql-platform-metadata-storage/tasks.md deleted file mode 100644 index a8fb996..0000000 --- a/openspec/changes/add-mysql-platform-metadata-storage/tasks.md +++ /dev/null @@ -1,23 +0,0 @@ -## 1. OpenSpec Artifacts - -- [x] 1.1 Create proposal, design, spec, and tasks for MySQL metadata storage configuration. -- [x] 1.2 Validate the change with `openspec validate add-mysql-platform-metadata-storage --strict`. - -## 2. MySQL Metadata Storage - -- [x] 2.1 Add platform config fields for `PLATFORM_MYSQL_DSN` and `PLATFORM_LOG_BODY_BACKEND`. -- [x] 2.2 Implement a MySQL-backed metadata snapshot store behind `repo.Store`. -- [x] 2.3 Wire router startup to support `PLATFORM_STORAGE_BACKEND=mysql`. -- [x] 2.4 Add tests for MySQL config loading, missing DSN failure, and log body backend selection. - -## 3. Documentation And Comments - -- [x] 3.1 Add commented MySQL examples to root/platform env examples and Docker compose. -- [x] 3.2 Update README docs with exact MySQL DSN examples and log storage guidance. - -## 4. Verification - -- [x] 4.1 Run `cd platform && go test ./config ./api ./repo -count=1`. -- [x] 4.2 Run `docker compose config`. -- [x] 4.3 Run `scripts/check-structure.sh`. -- [x] 4.4 Run `openspec validate add-mysql-platform-metadata-storage --strict`. diff --git a/openspec/changes/add-plugin-declared-remote-access/.openspec.yaml b/openspec/changes/add-plugin-declared-remote-access/.openspec.yaml deleted file mode 100644 index b119b63..0000000 --- a/openspec/changes/add-plugin-declared-remote-access/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-13 diff --git a/openspec/changes/add-plugin-declared-remote-access/design.md b/openspec/changes/add-plugin-declared-remote-access/design.md deleted file mode 100644 index 263b5eb..0000000 --- a/openspec/changes/add-plugin-declared-remote-access/design.md +++ /dev/null @@ -1,65 +0,0 @@ -## Context - -The platform already has separate run control, job, log, and artifact channels. Plugin manifests currently declare lifecycle, file, log, artifact, and AI capabilities, but they do not describe whether a server may be reached by FTP, rsync, or run, nor do they declare run-only operations such as remote database reads or RCON. The previous SCUM codebase used FTP/SFTP workers for file upload, scum_run for server-side operations, SQLite query forwarding, log transfer, and game-client command handling; this change brings those ideas into the new platform as typed, plugin-declared capabilities. - -## Goals / Non-Goals - -**Goals:** - -- Model remote access methods and remote run operations in the plugin manifest. -- Require installed plugins to declare remote capabilities before platform bridge or direct job dispatch can queue them. -- Keep remote file/database/RCON/log operations behind platform and run channels. -- Allow both SCUM and Minecraft plugins to declare compatible remote access needs. -- Keep job payloads bounded: logical target keys, scoped input/artifact refs, and safe result refs only. - -**Non-Goals:** - -- No raw FTP, rsync, database, or RCON credentials in plugin manifests or plugin pages. -- No browser-to-run, plugin-to-run, or plugin-to-database direct connection. -- No production FTP/rsync client, MySQL client, SQLite parser, or RCON protocol implementation in this change. -- No cloud host provider workflow or server rental/billing feature. - -## Decisions - -### Decision 1: Manifest declares methods and capabilities separately - -`remoteAccess.methods` describes how a server may be reached (`ftp`, `rsync`, `run`). `remoteAccess.runCapabilities` lists the exact run operations the plugin can use. This keeps transport choice visible while still preserving the existing run capability matching model. - -Alternative considered: encode everything as free-form tags. Rejected because marketplace and authorization need deterministic validation and filtering. - -### Decision 2: Remote access uses a dedicated bridge action - -Plugin pages use `remote.access.request` for database, RCON, log transfer, and remote file jobs. Existing `files.request` remains for generic scoped file operations; lifecycle `jobs.dispatch` remains for start/stop. The new action lets platform apply a distinct `server.remote.access` permission and capability declaration check. - -Alternative considered: overload `jobs.dispatch` for every remote operation. Rejected because RCON/database/log transfer should not be authorized only by `server.lifecycle`. - -### Decision 3: Direct server-bound jobs are plugin-gated - -When a job includes a `serverInstanceId`, platform validates the instance plugin declares the requested capability. This prevents callers from bypassing plugin bridge and enabling remote DB/RCON/log transfer jobs on plugins that did not opt in. - -Alternative considered: enforce declaration only in plugin bridge. Rejected because platform API callers can create jobs directly in tests and future admin flows. - -### Decision 4: Run remote executor is bounded metadata first - -Run reports remote capabilities and accepts remote jobs, but returns safe metadata/result refs rather than opening real network connections in this change. Real FTP/rsync/database/RCON adapters can later attach behind the same job envelopes without changing plugin manifests. - -Alternative considered: implement protocol clients now. Rejected because credential storage, network policy, and result artifact formats need a separate change and should not be rushed into manifest plumbing. - -## Risks / Trade-offs - -- [Risk] Operators may expect real FTP/rsync/DB/RCON execution immediately. Mitigation: docs and result messages state this is the declared, bounded job contract; adapter implementation remains a follow-up. -- [Risk] Capability names may grow numerous. Mitigation: keep them grouped under `remote.*` and validate them centrally. -- [Risk] Existing tests using generic jobs may fail once server-bound jobs are plugin-gated. Mitigation: lifecycle/file capabilities remain declared in existing test plugin fixtures. - -## Migration Plan - -1. Add OpenSpec, schema, domain, DTO, validator, and service support for remote access declarations. -2. Update SCUM and add Minecraft example manifests with declared remote access. -3. Extend run protocol and runtime to validate/report remote capabilities and complete bounded remote jobs. -4. Add tests for manifest validation, platform authorization, direct job gating, and run remote job handling. -5. Run manifest validation, platform/run tests, structure check, and strict OpenSpec validation. - -## Open Questions - -- Which follow-up change should own real FTP/rsync credential storage and connection testing? -- Should RCON results become log entries, artifacts, or both when full adapters land? diff --git a/openspec/changes/add-plugin-declared-remote-access/proposal.md b/openspec/changes/add-plugin-declared-remote-access/proposal.md deleted file mode 100644 index 417d18e..0000000 --- a/openspec/changes/add-plugin-declared-remote-access/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -The platform needs one safe model for remote game server access across hosted files, run-managed hosts, databases, logs, and RCON. The old SCUM stack handled FTP/SFTP, run-side process control, SQLite queries, logs, and client commands in separate paths; the new platform must make those abilities plugin-declared before they can be used. - -## What Changes - -- Add plugin manifest metadata for declared remote access methods: `ftp`, `rsync`, and `run`. -- Add remote run capabilities for remote file read/write, process start/stop, MySQL and SQLite query, log transfer, and RCON command dispatch. -- Add platform registry and marketplace projection fields so operators can see which remote access methods a game plugin enables. -- Add a platform-mediated bridge action for remote access requests that queues only capabilities declared by the installed plugin. -- Extend run capability reporting and bounded remote job handling so MC and SCUM plugins can opt into the same contract. -- Add first-party Minecraft and SCUM example manifests proving the shared declaration model. - -## Capabilities - -### New Capabilities - -- `plugin-declared-remote-access`: Plugin-declared remote server access through ftp, rsync, or run with scoped run jobs for remote files, lifecycle, database reads, log transfer, and RCON. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `plugins/` manifest schema, manifest validator tests, and first-party example manifests. -- Affects `platform/` domain, DTO, validators, service bridge dispatch, marketplace projections, docs, and tests. -- Affects `run/` protocol capability constants, job validation, runtime capability reporting, worker execution, docs, and tests. -- Does not add billing, cloud host sales, plugin-owned credentials, browser-to-run access, raw host paths, or raw database/RCON credentials in plugin pages. diff --git a/openspec/changes/add-plugin-declared-remote-access/specs/plugin-declared-remote-access/spec.md b/openspec/changes/add-plugin-declared-remote-access/specs/plugin-declared-remote-access/spec.md deleted file mode 100644 index e5d6257..0000000 --- a/openspec/changes/add-plugin-declared-remote-access/specs/plugin-declared-remote-access/spec.md +++ /dev/null @@ -1,56 +0,0 @@ -## ADDED Requirements - -### Requirement: Plugin manifests declare remote access methods -Game management plugin manifests SHALL declare remote access methods before platform exposes FTP, rsync, or run-mediated remote server operations for server instances created from that plugin. - -#### Scenario: Manifest declares supported methods -- **WHEN** a plugin manifest lists `ftp`, `rsync`, or `run` under remote access methods -- **THEN** platform registry and marketplace responses MUST preserve those methods without exposing host paths, remote credentials, direct sockets, or provider secrets - -#### Scenario: Unsafe remote access declaration is rejected -- **WHEN** a plugin manifest includes raw credentials, host paths, direct run socket details, or unknown remote access methods -- **THEN** plugin workspace validation and platform registration MUST reject the manifest before it becomes installable - -### Requirement: Remote run operations are capability gated -Remote run operations SHALL be represented as explicit run capabilities and SHALL require both the run endpoint and the installed game plugin to declare the requested capability. - -#### Scenario: Declared remote run job is queued -- **WHEN** a plugin declares a remote run capability and the selected run endpoint reports the same capability -- **THEN** platform MAY queue a bounded job for that server instance using logical target keys and scoped input or artifact refs - -#### Scenario: Undeclared remote run job is denied -- **WHEN** a caller requests remote database, RCON, log transfer, or remote file work for a server instance whose plugin did not declare the requested capability -- **THEN** platform MUST reject or deny the request before creating a job - -### Requirement: Remote access bridge is platform mediated -Plugin pages SHALL request remote access through a platform-mediated bridge action and MUST NOT connect directly to FTP, rsync, run, MySQL, SQLite, log storage, or RCON endpoints. - -#### Scenario: Bridge queues declared remote access -- **WHEN** a plugin page has `server.remote.access` permission and requests `remote.access.request` for a declared capability -- **THEN** platform MUST authorize the action and queue the corresponding bounded run job - -#### Scenario: Bridge denies undeclared remote access -- **WHEN** a plugin page requests `remote.access.request` for a capability not declared by the plugin -- **THEN** platform MUST return a safe denial and MUST NOT expose run credentials, host paths, database DSNs, RCON passwords, or remote storage endpoints - -### Requirement: Run handles bounded remote jobs -Run SHALL report supported remote capabilities and complete remote job assignments with bounded progress and safe result references while keeping control, job, logs, and artifact channels separate. - -#### Scenario: Run accepts remote database and RCON assignments -- **WHEN** run receives declared remote MySQL, SQLite, RCON, log transfer, or remote file assignments -- **THEN** run MUST validate bounded job metadata and return terminal results without embedding raw credentials, host paths, query result bodies, log bodies, or RCON output in the job result payload - -#### Scenario: Run rejects unsafe remote job payload -- **WHEN** a remote job assignment includes an absolute path, parent traversal, raw secret, direct socket, or oversized inline content -- **THEN** run MUST reject the assignment with a bounded failure result - -### Requirement: MC and SCUM plugins share the remote access model -First-party Minecraft and SCUM plugin manifests SHALL validate against the same remote access schema and declare only the capabilities each game needs. - -#### Scenario: Minecraft plugin validates -- **WHEN** the Minecraft example plugin declares run-managed files, logs, and RCON access -- **THEN** plugin workspace manifest validation MUST pass - -#### Scenario: SCUM plugin validates -- **WHEN** the SCUM example plugin declares FTP/rsync/run file access, run lifecycle, SQLite/MySQL database read compatibility, log transfer, and RCON access -- **THEN** plugin workspace manifest validation MUST pass diff --git a/openspec/changes/add-plugin-declared-remote-access/tasks.md b/openspec/changes/add-plugin-declared-remote-access/tasks.md deleted file mode 100644 index 5726c5d..0000000 --- a/openspec/changes/add-plugin-declared-remote-access/tasks.md +++ /dev/null @@ -1,40 +0,0 @@ -## 1. OpenSpec Contracts - -- [x] 1.1 Create proposal, design, and spec for plugin-declared remote access. -- [x] 1.2 Validate the new change artifacts with `openspec validate --strict`. - -## 2. Plugin Manifest Contracts - -- [x] 2.1 Extend the game plugin manifest schema with `remoteAccess` methods, run capabilities, database engines, RCON, and log transfer declarations. -- [x] 2.2 Update manifest validation tests and examples so SCUM and Minecraft validate with remote access declarations. - -## 3. Platform Registry and Authorization - -- [x] 3.1 Add domain and DTO remote access metadata to game plugin, manifest, and marketplace projections. -- [x] 3.2 Extend platform validators for remote methods, remote run capabilities, `server.remote.access`, and `remote.access.request`. -- [x] 3.3 Gate server-bound jobs against the server instance plugin's declared capabilities. -- [x] 3.4 Add bridge execution for declared remote access requests and tests for denied undeclared capabilities. - -## 4. Run Protocol and Worker - -- [x] 4.1 Add remote run capability constants and validation rules for bounded remote assignments. -- [x] 4.2 Report remote capabilities from run smoke and worker mode. -- [x] 4.3 Add bounded remote job execution results that keep heavy payloads in artifact/log channels. -- [x] 4.4 Add run tests for remote DB/RCON/log/file capabilities and unsafe payload rejection. - -## 5. Documentation and Verification - -- [x] 5.1 Update platform, run, and plugin docs for remote access capability declarations. -- [x] 5.2 Run plugin manifest validation, platform tests, run tests, and `scripts/check-structure.sh`. -- [x] 5.3 Record verification evidence in `tasks.md`. - -## Evidence - -- `openspec validate add-plugin-declared-remote-access --strict` - passed; PostHog telemetry flush logged DNS warnings only after validation succeeded. -- `go test ./...` in `platform/` - passed. -- `go test ./...` in `run/` - passed. -- `npm test` in `plugins/` - passed: 14 manifest/SDK tests. -- `npm run typecheck` in `plugins/` - passed. -- `TMPDIR=/private/tmp npm run validate:manifest` in `plugins/` - passed for dev, SCUM, and Minecraft manifests. -- `scripts/check-structure.sh` - passed. -- `git diff --check` - passed. diff --git a/openspec/changes/add-run-distribution-and-client-managers/.openspec.yaml b/openspec/changes/add-run-distribution-and-client-managers/.openspec.yaml deleted file mode 100644 index b119b63..0000000 --- a/openspec/changes/add-run-distribution-and-client-managers/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-13 diff --git a/openspec/changes/add-run-distribution-and-client-managers/design.md b/openspec/changes/add-run-distribution-and-client-managers/design.md deleted file mode 100644 index fb71b57..0000000 --- a/openspec/changes/add-run-distribution-and-client-managers/design.md +++ /dev/null @@ -1,107 +0,0 @@ -## Context - -The platform already has control, job, log, and artifact channels, plus plugin-declared remote access capabilities for FTP, rsync, run-mediated files, SQL, logs, and RCON. The missing layer is operational delivery: operators need to generate a per-server run executable, download it for a selected OS/architecture, authenticate it with the server's current database-backed run key, update it after it is online, and let plugins declare how run finds and controls the actual game server. - -The old SCUM stack has useful patterns: a run-side process controller, a separate client/robot manager for cases where server control requires a game client or custom automation, Git-based source updates, Go toolchain checks, FTP log polling, SQLite reads, and command forwarding. This design generalizes those patterns without making SCUM special in platform core. - -## Goals / Non-Goals - -**Goals:** - -- Let server owners generate and download a server-scoped run package from the server list or server action menu. -- Store one current run key and one current client-manager key per server/component in the database, encrypted at rest, with redacted secret references in APIs and logs. -- Let online run endpoints self-update through a platform job that stages, verifies, swaps, and reports rollback-safe status. -- Let plugins declare runtime profiles that explain how to find a server, start/stop it, check dependencies, install missing dependencies, collect live and historical logs, and expose files, FTP/rsync, SQL, and RCON. -- Let plugins declare optional client-manager build profiles for cases like SCUM where a separate executable must be compiled from an open repository with a distinct authentication key. -- Keep all browser and plugin operations platform-mediated and preserve channel isolation for control, jobs, logs, files, and artifacts. - -**Non-Goals:** - -- No billing, cloud host sales, server rental marketplace, or cloud-provider provisioning workflow. -- No raw run keys, client-manager keys, FTP passwords, database DSNs, RCON passwords, direct sockets, or host paths in plugin pages or platform_web responses. -- No arbitrary plugin shell scripts for dependency installation or build execution. -- No requirement that every game use run; FTP-only, FTP+RCON, and custom-client modes remain valid when declared by the plugin. -- No production package-signing infrastructure beyond local checksums/signature hooks in the first implementation. - -## Decisions - -### Decision 1: Distribution artifacts are secret-bearing platform records - -Platform stores generated run and client-manager packages as artifacts with a distribution record containing kind, server instance, plugin ID, target OS/architecture, source revision, checksum, status, component kind, and key generation. The package config contains the current active key for that server/component because the remote executable must authenticate with it. API responses, logs, job payloads, plugin SDK payloads, and UI state expose only artifact IDs, key generation, and redacted secret refs. - -Alternative considered: ship one global run binary and ask users to hand-edit config files. Rejected because it causes copy/paste key exposure, weak auditability, and poor operator experience. - -Distribution generation is asynchronous. Platform creates a bounded `distribution.build` job on the assigned run endpoint and returns a `building` distribution with the real job ID. The run worker obtains the secret-bearing build input only through its authenticated leased-job channel, builds in an isolated workspace, and uploads the resulting archive through the artifact channel. Platform marks a distribution `available` only after the job succeeds and the referenced artifact is present and available. A JSON build plan, generated config, or synthetic build log is never a downloadable distribution artifact. - -Run packages are built from the trusted run worker checkout. Plugin-declared client managers are checked out from the approved HTTPS Git repository and revision carried by the build job. Build execution uses a fixed build-system adapter and target tuple; repository content cannot supply arbitrary platform-side commands. - -### Decision 2: Run and client-manager credentials are separate singleton keys - -Each server/component has exactly one active run key and, when needed, exactly one active client-manager key. Run and client-manager keys remain different secrets, but platform does not keep multiple simultaneously valid keys for the same component. Resetting a key replaces the encrypted database value, increments the key generation, invalidates every older run or client package for that component, and requires regenerating and redeploying the affected package. - -Alternative considered: reuse the run key for plugin client managers. Rejected because client managers may have broader game-specific control surfaces and need independent reset, revocation, and audit. - -Alternative considered: issue temporary enrollment tokens and exchange them for runtime keys. Rejected because the desired operator model is a single database-backed current key per server/component, with explicit reset when trust must be revoked. - -### Decision 3: Plugins declare runtime profiles, not raw local paths - -Plugins define runtime profiles with logical server discovery probes, lifecycle actions, dependency probes, dependency install plans, log sources, transport profiles, and optional client-manager profiles. Operators bind those logical keys to a server instance at setup time. Run resolves bindings locally and returns logical status, not raw host paths. - -Alternative considered: hardcode Minecraft and SCUM discovery rules into platform. Rejected because the platform should remain a game server management core, while game-specific control knowledge belongs in plugins. - -### Decision 4: Dependency install is typed and reviewable - -Dependency checks are read-only probes such as command version, service existence, port availability, file presence under scoped roots, Steam app presence, Java version, Docker availability, or Windows package presence. Install plans are typed steps with approved package managers or verified downloads. Platform shows the plan and queues it only after operator approval. - -Alternative considered: let plugins provide arbitrary install shell scripts. Rejected because it creates an unrestricted execution path and conflicts with run security rules. - -### Decision 5: Client-manager builds are source-pinned build jobs - -Plugin client-manager profiles declare a repository URL, branch/tag or pinned revision policy, supported targets, build system, config template keys, dependency hints, and produced artifact paths. Platform creates a build job in an isolated workspace or future build worker, redacts secrets from logs, writes generated config from secret refs, and publishes a downloadable artifact. - -Alternative considered: require plugin authors to upload prebuilt binaries only. Rejected because SCUM-style managers need reproducible platform-side injection of per-server config and keys. - -### Decision 6: Run owns local execution and transport adapters - -Run is the multi-platform server launcher and transport agent. It starts/stops third-party programs through plugin lifecycle profiles, tails stdout/stderr and declared log files, transfers files through the artifact channel, and performs declared FTP/rsync, SQL, and RCON operations through scoped adapters. Long transfers must not block heartbeat, job ack/result, or log ingest. - -Alternative considered: let platform_web or plugin pages connect directly to FTP/RCON/SQL. Rejected because that exposes credentials and bypasses platform authorization, audit, and channel isolation. - -### Decision 7: Live and historical logs share durable sequence semantics - -Run streams live process output and file tails through the log channel with source IDs and monotonically increasing sequences. Historical logs are fetched through backfill jobs that use checkpoints, file fingerprints, FTP polling cursors, or database cursors to avoid duplicates. Platform_web displays live tail and history through platform APIs only. - -Alternative considered: return log bodies inside job results. Rejected because logs can be large and must not compete with control and job metadata. - -### Decision 8: Self-update is staged and rollback-safe - -When run is online and supports the update capability, platform queues an update job with an artifact ref and checksum. Run downloads via the artifact channel, verifies checksum/signature metadata, stages the new binary, drains or rejects new local work, swaps atomically where supported, restarts, and reports success or rollback failure. Offline endpoints keep the latest downloadable package for manual replacement. - -Alternative considered: send a raw command to pull the latest repository. Rejected because it is platform-specific, hard to verify, and unsafe to audit. - -## Risks / Trade-offs - -- [Risk] Build jobs can execute untrusted repository code. Mitigation: require plugin-declared build profiles, pinned refs, isolated workspaces, bounded logs, and a later sandboxed build worker before public plugin builds. -- [Risk] Self-update can leave a remote run offline. Mitigation: stage artifacts, verify checksums, retain previous binary, report last-known version, and keep manual download available. -- [Risk] Dependency install plans differ across Windows, Linux, and macOS. Mitigation: model probes and install steps per target platform, and show unsupported targets before dispatch. -- [Risk] Historical log backfill can duplicate records. Mitigation: persist per-source checkpoints with file fingerprints, offsets, sequence acknowledgements, and cursor metadata. -- [Risk] Resetting a key immediately breaks deployed run or client-manager binaries. Mitigation: make reset confirmations explicit, mark old packages revoked, show that regeneration is required, and keep online update/manual download paths visible. -- [Risk] Operators may expect plugin profiles to work without binding server-specific values. Mitigation: require server setup validation before showing actions that depend on missing bindings. - -## Migration Plan - -1. Extend plugin manifest schema and examples with runtime profiles, dependency probes, log sources, transport profiles, and client-manager build profiles. -2. Add platform domain, model, DTO, validator, and service support for distribution records, encrypted database key storage, key reset, key generation checks, build jobs, dependency status, runtime bindings, and update jobs. -3. Add run package config loading, registration authentication against the current database key generation, dependency probes, discovery probes, log source checkpoints, transport adapter envelopes, and self-update executor. -4. Add platform_web server-list and server-detail action menus for generate/download/update run, generate/download client manager, dependency checks, live logs, and historical logs. -5. Add tests and docs across plugins, platform, run, and platform_web; validate with OpenSpec, structure checks, backend tests, plugin validation, run tests, frontend tests, and browser walkthroughs for touched UI. - -Rollback removes the new distribution/client-manager routes and UI actions, leaves existing run endpoints and plugin-declared remote access behavior intact, and preserves already-created artifacts as inert downloadable records until manually cleaned. - -## Open Questions - -- Which envelope-key source should protect the encrypted key columns stored in the platform database? -- Should platform-side builds run in the platform process for the first version, or require a separate build worker from day one? -- What default retention should apply to historical logs and generated binaries? -- Which target packaging formats should be first-class first: zip/tar.gz only, or Windows service installer, systemd unit bundle, and launchd plist bundles? -- Should plugin client-manager profiles support private repositories later, and if so how should source credentials be stored and audited? diff --git a/openspec/changes/add-run-distribution-and-client-managers/proposal.md b/openspec/changes/add-run-distribution-and-client-managers/proposal.md deleted file mode 100644 index 2bd9fcd..0000000 --- a/openspec/changes/add-run-distribution-and-client-managers/proposal.md +++ /dev/null @@ -1,30 +0,0 @@ -## Why - -Run is becoming the platform's cross-platform server launcher and transport agent, but operators still need a safe way to generate, download, authenticate, update, and observe per-server run binaries. Some games also need plugin-declared companion clients, such as SCUM-style client managers, that are built from plugin-provided source repositories with separate credentials and lifecycle from run. - -## What Changes - -- Add a platform-managed run distribution workflow from the server list or server actions menu: generate executor, choose target OS/architecture, write the current server/component authentication key into the generated package config, download the artifact, and store the single active encrypted key in the database. -- Add run self-update orchestration: if an assigned run is already online, platform can enqueue an update command that instructs the remote run to pull or replace itself with the latest approved run build. -- Add plugin-declared external controller/client-manager build profiles for games that cannot be controlled only through run, FTP, SQL, logs, and RCON. -- Add separate database-backed active keys for run and plugin-declared client managers; resetting a server's run or client key invalidates every previous package and requires regenerating the corresponding run or client artifact. -- Extend plugin manifests with server discovery, dependency checks, dependency install guidance, log source declarations, historical log retention hints, and transport wiring so run knows how to find and manage the target server without hardcoded game logic. -- Add UI/API contracts for server action menu entries: generate run, download latest run package, push run update, generate plugin client manager, inspect dependency status, open live logs, and browse historical logs. - -## Capabilities - -### New Capabilities - -- `run-distribution-and-client-managers`: Platform-managed run packaging, download, key provisioning, remote self-update, plugin-declared companion client builds, dependency checks, server discovery, and live/historical observability. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `plugins/` manifest schema, SDK docs, SCUM and Minecraft example manifests, and validation tests. -- Affects `platform/` domain, DTOs, encrypted secret references, build records, artifact records, run endpoint provisioning, job creation, audit events, and API routes. -- Affects `run/` generated package config, updater job handling, dependency probes, server discovery adapters, live log tailing, historical log checkpoints, file/FTP/SQL/RCON transport adapters, and docs. -- Affects `platform_web/` server list and server detail actions, live log views, historical log views, dependency status surfaces, and browser walkthrough coverage. -- Does not add billing, host rental, cloud host sales, raw credential exposure, plugin-owned direct sockets, or unrelated SaaS marketplace workflows. diff --git a/openspec/changes/add-run-distribution-and-client-managers/specs/run-distribution-and-client-managers/spec.md b/openspec/changes/add-run-distribution-and-client-managers/specs/run-distribution-and-client-managers/spec.md deleted file mode 100644 index 9d19cce..0000000 --- a/openspec/changes/add-run-distribution-and-client-managers/specs/run-distribution-and-client-managers/spec.md +++ /dev/null @@ -1,141 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform generates server-scoped run packages -The platform SHALL let an authorized operator generate a run package for a selected server instance and target platform using the server's single current run key stored encrypted in the platform database. - -#### Scenario: Operator generates run from server actions -- **WHEN** an authorized operator selects generate executor for a server instance and chooses a supported OS/architecture -- **THEN** platform MUST create or reuse the server's current encrypted run key, queue a real run-worker build job, return a building distribution with its job ID, compile the target executable, package it with the generated config, publish the completed binary archive and checksum through the artifact channel, record an audit event, and MUST NOT return the raw key in the API response - -#### Scenario: Run generation has not completed -- **WHEN** the run-worker build job is queued, running, failed, or cancelled -- **THEN** platform MUST keep the distribution unavailable for download, expose the real job state and progress, and MUST NOT substitute generated configuration JSON or a synthetic build log as the downloadable run package - -#### Scenario: Operator downloads generated run package -- **WHEN** an authorized operator downloads a generated run artifact -- **THEN** platform MUST authorize access by server scope and return the secret-bearing package through artifact/download APIs without exposing raw host paths, direct sockets, or database credentials in package metadata - -### Requirement: Run and client-manager keys are isolated singletons -Run executors and plugin-declared client managers SHALL use different authentication secrets, and each server/component SHALL have exactly one current active key stored encrypted in the platform database. - -#### Scenario: Client manager is generated after run -- **WHEN** a plugin-declared client-manager package is generated for a server that already has a run package -- **THEN** platform MUST create or reuse the server's current encrypted client-manager key and MUST NOT reuse, reveal through API metadata, or derive it from the run key - -#### Scenario: Component key reset is requested -- **WHEN** an operator resets a server's run key or client-manager key -- **THEN** platform MUST replace the encrypted database key for that component, increment the key generation, revoke all packages generated with prior generations, and record an audit event identifying the component kind without logging raw key material - -#### Scenario: Old package authenticates after reset -- **WHEN** a run or client-manager package generated before the latest key reset attempts to authenticate -- **THEN** platform MUST reject the old key and require the operator to regenerate and redeploy the corresponding run or client-manager package - -### Requirement: Plugins declare runtime profiles -Game plugins SHALL declare runtime profiles that describe how run or external transports can discover, control, observe, and connect to the game server using logical keys instead of raw credentials or host paths. - -#### Scenario: Plugin declares server discovery and transports -- **WHEN** a plugin manifest declares runtime profiles with discovery probes, lifecycle actions, dependency probes, log sources, FTP/rsync, SQL, RCON, or client-manager bindings -- **THEN** plugin validation MUST accept only known profile fields and platform MUST preserve the declarations for server setup and action gating - -#### Scenario: Plugin manifest includes unsafe runtime details -- **WHEN** a plugin manifest embeds raw run keys, client-manager keys, FTP passwords, database DSNs, RCON passwords, absolute host paths, direct sockets, or arbitrary shell scripts in runtime profiles -- **THEN** plugin validation and platform registration MUST reject the manifest before it becomes installable - -### Requirement: Server runtime bindings resolve plugin profiles -Server instances SHALL bind plugin-declared logical runtime keys to operator-provided local or remote settings before run-dependent actions are enabled. - -#### Scenario: Run discovers an existing server -- **WHEN** run registers for a server instance with complete runtime bindings and the plugin declares discovery probes -- **THEN** run MUST evaluate the probes locally, report logical discovery status, version, and health, and MUST NOT return raw host paths or secret values to platform_web or plugin pages - -#### Scenario: Required binding is missing -- **WHEN** an operator opens server actions for a runtime profile that requires unbound install root, service name, FTP profile, SQL profile, RCON profile, or client-manager profile -- **THEN** platform MUST mark dependent actions unavailable and explain the missing logical binding without exposing internal storage details - -### Requirement: Plugins control third-party startup behavior -Third-party game program startup and shutdown SHALL be driven by plugin-declared lifecycle profiles and server runtime bindings, not hardcoded platform game logic. - -#### Scenario: Local process server starts through run -- **WHEN** a plugin declares a local process lifecycle profile and the server runtime binding is complete -- **THEN** run MUST start or stop the third-party program through the declared bounded action template and stream stdout/stderr through the log channel - -#### Scenario: Hosted server has no local lifecycle control -- **WHEN** a plugin declares an FTP-only, FTP+RCON, or custom-client runtime profile without local process lifecycle -- **THEN** platform MUST hide or deny local start/stop actions and expose only the declared remote or client-manager actions - -### Requirement: Dependency checks and installs are typed -Run SHALL check dependencies through plugin-declared probes and SHALL install missing dependencies only through approved, typed, reviewable install plans. - -#### Scenario: Dependency check reports missing runtime -- **WHEN** run evaluates plugin-declared dependency probes and a required runtime, service, package, toolchain, Steam app, Java runtime, Docker runtime, or OS package is missing -- **THEN** platform MUST show dependency status and a reviewable install plan if the plugin declares one for the target platform - -#### Scenario: Dependency install is approved -- **WHEN** an authorized operator approves a dependency install plan -- **THEN** platform MUST queue a bounded run job using typed package/download steps, checksum or source metadata where provided, and must reject arbitrary shell snippets - -### Requirement: Client-manager packages are plugin-declared builds -The platform SHALL support plugin-declared client-manager build profiles for companion executables that require source checkout, configuration injection, and compilation before download. - -#### Scenario: SCUM-style client manager is generated -- **WHEN** a plugin declares a client-manager build profile with repository, revision policy, supported target platform, build system, config template, and output artifact paths -- **THEN** platform MUST create a run-worker build job that checks out the approved source and revision, injects configuration obtained through the authenticated job-input channel, compiles the target executable, uploads the downloadable artifact through the artifact channel, and redacts secrets and workspace paths from progress and build results - -#### Scenario: Client-manager build is still running -- **WHEN** the source checkout, environment check, dependency download, compile, or artifact upload stage is incomplete -- **THEN** platform_web MUST display the corresponding real job progress and MUST NOT mark later stages complete on a local timer - -#### Scenario: Unsupported client-manager target is requested -- **WHEN** an operator requests a client-manager build for an OS/architecture not declared by the plugin profile -- **THEN** platform MUST reject the request before cloning source or creating a credential - -### Requirement: Online run endpoints self-update through platform jobs -The platform SHALL update online run endpoints by queuing a bounded self-update job that references an approved artifact and checksum instead of sending raw shell commands. - -#### Scenario: Online run accepts update -- **WHEN** an assigned run endpoint is online and supports self-update -- **THEN** platform MUST queue an update job, and run MUST download the artifact, verify checksum or signature metadata, stage the replacement, report progress, and restart or swap only after verification succeeds - -#### Scenario: Update verification fails -- **WHEN** run cannot verify or stage the update artifact -- **THEN** run MUST keep the current executable, report a bounded failure result, and preserve heartbeat or last-known status without leaking local paths or credentials - -### Requirement: Run exposes multi-channel transport capabilities -Run SHALL provide declared server operations through separate control, job, log, artifact/file, FTP/rsync, SQL, and RCON channels or adapters while preserving priority and bounded payload rules. - -#### Scenario: Long file transfer is active -- **WHEN** run is transferring a large file, FTP payload, rsync payload, or artifact chunk -- **THEN** heartbeat, job ack/result, cancellation polling, and log ingest MUST continue independently without waiting for transfer completion - -#### Scenario: Undeclared transport operation is requested -- **WHEN** a plugin page or platform caller requests FTP, SQL, RCON, file, or process work not declared by the plugin runtime profile and run capabilities -- **THEN** platform MUST deny the request before creating a job and MUST return only a safe error - -### Requirement: Live and historical logs are platform-mediated -Platform_web SHALL show live and historical server logs through platform APIs backed by run log ingest, log checkpoints, and plugin-declared log sources. - -#### Scenario: Live log tail is opened -- **WHEN** an operator opens live logs for a server with an online run endpoint -- **THEN** platform_web MUST read from platform log APIs and display sequenced log entries from run without direct run sockets or raw file paths - -#### Scenario: Historical log backfill is requested -- **WHEN** an operator requests older logs for a declared process, file, FTP, SQL, or plugin-specific log source -- **THEN** platform MUST queue or serve a backfill using per-source checkpoints, cursors, or file fingerprints and MUST avoid embedding large log bodies in job result payloads - -### Requirement: Server action menu reflects declared availability -The server list and server detail action menu SHALL show generate run, download run, push run update, generate client manager, dependency check, install dependency, live logs, and historical logs only when the current user and plugin/runtime state permit them. - -#### Scenario: Actions are available -- **WHEN** a server has a plugin with runtime profiles, complete bindings, and the current user has the required permissions -- **THEN** platform_web MUST render the applicable actions using the existing game-operations console style and call platform APIs rather than plugin-owned direct endpoints - -#### Scenario: Actions are unavailable -- **WHEN** a server lacks a required plugin declaration, binding, online run status, build profile, or user permission -- **THEN** platform_web MUST hide or disable the action with a safe reason and MUST NOT render raw credentials, host paths, direct sockets, or secret refs beyond redacted identifiers - -### Requirement: Distribution and control events are audited -The platform SHALL audit run generation, run download, credential reset, run update, dependency install, client-manager build, client-manager download, and transport operation requests. - -#### Scenario: Sensitive operation completes -- **WHEN** a sensitive distribution or control operation succeeds, fails, or is denied -- **THEN** platform MUST record actor, server instance, plugin ID, component kind, operation, result, artifact ID or job ID where applicable, and redacted reason without raw secret material diff --git a/openspec/changes/add-run-distribution-and-client-managers/tasks.md b/openspec/changes/add-run-distribution-and-client-managers/tasks.md deleted file mode 100644 index e3f3e4a..0000000 --- a/openspec/changes/add-run-distribution-and-client-managers/tasks.md +++ /dev/null @@ -1,92 +0,0 @@ -## 1. Plugin Contracts - -- [x] 1.1 Extend the game plugin manifest schema with runtime profiles for server discovery, lifecycle profiles, dependency probes, install plans, log sources, transport profiles, and client-manager build profiles. -- [x] 1.2 Update plugin SDK types and bridge docs for runtime bindings, run distribution actions, dependency actions, live/historical log actions, and client-manager generation. -- [x] 1.3 Update SCUM and Minecraft example manifests to declare realistic runtime profiles, dependency probes, log sources, and transport bindings. -- [x] 1.4 Add plugin validation tests that accept safe runtime/client-manager profiles and reject raw keys, database DSNs, RCON passwords, direct sockets, raw host paths, and arbitrary shell snippets. - -## 2. Platform Secret and Distribution Model - -- [x] 2.1 Add platform domain, DTO, model, repository, and validator types for run distributions, client-manager distributions, runtime bindings, encrypted database keys, key generations, dependency status, build jobs, and update jobs. -- [x] 2.2 Implement encrypted-at-rest database key storage for exactly one active run key and one active client-manager key per server/component. -- [x] 2.3 Add key reset services and tests proving reset replaces the active encrypted key, increments generation, revokes old packages, and requires regenerating the affected run or client-manager artifact. -- [x] 2.4 Add services and tests for generating server-scoped run packages, creating artifact records, writing the current run key into package config, storing key generation metadata, and authorizing downloads. -- [x] 2.5 Add services and tests for client-manager build records, distinct current client-manager keys, source revision metadata, target-platform validation, and downloadable artifact publication. -- [x] 2.6 Add audit events for generation, download, key reset, update, dependency install, build, and denied sensitive operations. - -## 3. Platform APIs and Job Orchestration - -- [x] 3.1 Add API routes and handlers for server action availability, generate/download run, reset run key, push run update, generate/download client manager, reset client-manager key, check dependencies, install dependencies, live logs, and historical logs. -- [x] 3.2 Gate all routes by user permissions, server ownership, plugin declarations, runtime bindings, and run endpoint capability availability. -- [x] 3.3 Add self-update job creation with artifact refs, checksums, idempotency keys, and denied-path tests for offline or unsupported run endpoints. -- [x] 3.4 Add dependency-check and dependency-install job creation with typed install plans and rejection of arbitrary shell commands. -- [x] 3.5 Add historical log backfill job creation with source IDs, checkpoints, bounded results, and artifact/log channel separation. - -## 4. Run Bootstrap and Runtime Behavior - -- [x] 4.1 Implement generated package config loading, current key generation authentication, server-scoped identity, old-package rejection after reset, and redacted local diagnostics. -- [x] 4.2 Implement runtime profile resolution for local process, hosted FTP/RCON, FTP-only, SQL, file, and custom-client modes using server bindings. -- [x] 4.3 Implement dependency probes and typed install plan execution for supported OS targets with safe progress and failure results. -- [x] 4.4 Implement self-update executor with artifact download, checksum/signature verification hooks, staging, rollback, restart/swap behavior, and tests. -- [x] 4.5 Implement live log tailing from process stdout/stderr and declared file sources with durable sequence checkpoints. -- [x] 4.6 Implement historical log backfill cursors for declared file, FTP, SQL, and plugin-specific log sources without embedding large log bodies in job results. -- [x] 4.7 Implement bounded adapter envelopes for FTP/rsync, SQL read, RCON command, and file transfer so long transfers do not block heartbeat or job metadata. - -## 5. Build Pipeline - -- [x] 5.1 Implement platform-side or build-worker source checkout for plugin-declared client-manager repositories with pinned revision metadata. -- [x] 5.2 Implement target-platform validation, build dependency checks, redacted config injection, bounded build logs, checksum calculation, and artifact publication. -- [x] 5.3 Add SCUM-style client-manager build tests covering supported targets, unsupported target denial, separate credential injection, and redacted logs. - -## 6. platform_web Workflows - -- [x] 6.1 Add server list and server detail action menu entries for generate run, download run, push run update, generate client manager, dependency check/install, live logs, and historical logs. -- [x] 6.2 Add API client types, schemas, and tests for run distribution, client-manager distribution, dependency status, update jobs, and historical log backfill. -- [x] 6.3 Add UI states for unavailable actions with safe reasons, redacted secret refs, run online/offline status, build status, update progress, dependency status, and log backfill status. -- [x] 6.4 Preserve the existing platform_web game-operations console style and verify no raw keys, host paths, sockets, DSNs, RCON passwords, or direct run endpoints render in the UI. - -## 7. Documentation and Verification - -- [x] 7.1 Update platform, run, plugin, and platform_web docs for run generation, client-manager builds, runtime bindings, dependency profiles, live logs, historical logs, and self-update behavior. -- [x] 7.2 Run plugin manifest validation, plugin SDK tests, platform tests, run tests, frontend tests, and `scripts/check-structure.sh`. -- [x] 7.3 Run `openspec validate add-run-distribution-and-client-managers --strict`. -- [x] 7.4 Complete a browser walkthrough for touched server-list and server-detail workflows before marking UI acceptance complete. -- [x] 7.5 Record verification evidence in this task file before completion. - -## 8. Real Distribution Build Repair - -- [x] 8.1 Replace synchronous synthetic run/client artifacts with queued `distribution.build` jobs, building distribution records, authenticated build-input retrieval, and terminal job projection. -- [x] 8.2 Implement the independent run worker build adapter for trusted run source and approved HTTPS client-manager repositories, including fixed Go builds, isolated workspaces, config packaging, checksums, and chunked artifact upload. -- [x] 8.3 Drive the platform_web generation dialog from real job progress and terminal state instead of timer-completed stages. -- [x] 8.4 Add regression coverage proving generation queues a backend job, does not publish JSON plans as artifacts, publishes only uploaded build output, and reports actual progress/failure. -- [x] 8.5 Run focused platform, run, frontend, OpenSpec, and structure verification and record the evidence below. - -## Verification Evidence - -- `cd plugins && npm run validate:manifest`: passed. First sandbox attempt failed with `listen EPERM` on the local `tsx` IPC pipe, then the same command passed with approved escalation. -- `cd plugins && npm run typecheck`: passed. -- `cd plugins && npm test`: passed, 1 file / 17 tests. -- `cd run && go test ./...`: passed. -- `cd platform && go test ./...`: passed. -- `cd platform_web && npm run typecheck`: passed. -- `cd platform_web && npm test`: passed, 13 files / 60 tests. -- `cd platform_web && npm run build`: passed, Vite production build completed. -- `scripts/check-structure.sh`: passed. -- `openspec validate add-run-distribution-and-client-managers --strict`: passed. The CLI printed a PostHog network flush warning after validation, but exited successfully with `Change 'add-run-distribution-and-client-managers' is valid`. -- `LOCAL_DEBUG_PLATFORM_PORT=18189 LOCAL_DEBUG_WEB_PORT=5183 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-acceptance scripts/browser-acceptance.sh`: passed. -- Browser evidence file: `/private/tmp/browser-local-debug-acceptance/browser-acceptance/browser-acceptance-evidence.json`. -- Browser walkthrough evidence covered 首页、服务器管理、服务器管理 / 运行操作菜单、插件市场、用户管理、AI 提供商管理、服务器详情、服务器详情 / 插件控制, plus desktop/mobile checks for black mecha and magical-girl themes. - -### Real Distribution Build Repair Evidence (2026-07-17) - -- `cd platform && go test ./service ./validator ./api`: passed. -- `cd run && go test ./runtime ./protocol ./api`: passed; the runtime suite compiled a real run executable from an isolated source copy and uploaded the archive through chunked artifact calls. -- `cd platform_web && npm test -- --run components/RuntimeTaskProgress.test.ts pages/ConsolePages.test.tsx`: passed, 2 files / 13 tests. -- `cd platform && go test ./...`: passed across all platform packages. -- `cd run && go test ./...`: passed across all independent run packages. -- `cd platform_web && npm test`: passed, 15 files / 76 tests. -- `cd platform_web && npm run typecheck`: passed. -- `cd platform_web && npm run build`: passed, Vite production build completed. -- `openspec validate add-run-distribution-and-client-managers --strict`: passed. -- `scripts/check-structure.sh`: passed. -- Regression evidence covers queued `distribution.build` creation with no synthetic artifact, authenticated leased build-input retrieval, rejection of premature success before artifact upload, successful retry after real artifact publication, retained downloadable chunk payloads, isolated trusted run source copies, rejection of credential-bearing/unpinned client repositories, and frontend projection of real running/succeeded/failed job states. diff --git a/openspec/changes/add-scum-file-config-workbench/.openspec.yaml b/openspec/changes/add-scum-file-config-workbench/.openspec.yaml deleted file mode 100644 index e8209ff..0000000 --- a/openspec/changes/add-scum-file-config-workbench/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-28 diff --git a/openspec/changes/add-scum-file-config-workbench/design.md b/openspec/changes/add-scum-file-config-workbench/design.md deleted file mode 100644 index a583f65..0000000 --- a/openspec/changes/add-scum-file-config-workbench/design.md +++ /dev/null @@ -1,48 +0,0 @@ -## Context - -The SCUM plugin already declares runtime config mappings and log sources, while Platform exposes safe configuration read/diff/approval APIs and a scoped file-operation dispatcher. Its current plugin page is an operations overview with semantic logs; this does not match the operator's configuration-first workflow. - -## Goals / Non-Goals - -**Goals:** - -- Make plugin-owned logical file declarations the single catalog for SCUM configuration and log file scopes. -- Model safe configuration fields separately from the raw INI content, then compose changes into the existing Platform diff-preview and approval flow. -- Present declared log files and their Platform log-stream contents in the same workbench. -- Redirect historical SCUM page keys to the default `files-config` page without breaking shared plugin routing. - -**Non-Goals:** - -- Arbitrary filesystem browsing, path entry, terminals, FTP/rsync controls, raw secrets, or host/socket exposure. -- Raw text as the default configuration editor, direct write access for unknown fields, or a new run-side protocol. -- Changing global server-list behavior or non-SCUM plugin pages. - -## Decisions - -1. Add `fileWorkspace` to the plugin manifest/runtime projection. It contains safe logical directories/files and modeled fields, rather than host paths or unbounded schemas. This makes a page contract auditable and keeps ownership with the plugin. -2. Give every modeled field an owning logical file key, Chinese operational metadata, and constrained control details. The frontend uses only these fields to compose proposed INI content; unmapped lines remain visible as read-only field records. -3. Reuse declared file read/write dispatch for logical file keys. A completed `files.read` job may be projected through a Platform-owned declared-file snapshot endpoint, which accepts only a declared logical file key, returns no host path or job payload, and redacts secret-like assignment values before returning content. Modeled edits and optional raw config edits require a visible diff preview before the Platform queues a declared `files.write` job. -4. Move SCUM from `operations` to `files-config`; the route resolver redirects old `overview`, `config`, `logs`, and `operations` keys only for `game.scum`. Other plugins keep their declared page keys unchanged. -5. Build page content from existing shared console form/list/diff classes. No page-owned surface system or global decoration is added. -6. Treat the SCUM workbench as one selected logical file at a time. The left pane contains only declared directories and files. The right pane presents either modeled fields or an optional raw mode for an editable configuration file, and a read-only log view for a log file. Selection changes must not trigger unbounded polling or path-based requests. -7. Render the plugin bundle in embedded mode when it is mounted under a server-detail section. The generic plugin page frame and host-context diagnostics remain available for direct plugin routes, but must not be nested inside the file-management tab. -8. Keep Companion player, reward, state, vehicle, and trajectory controls out of this page. Those remain available through their separately scoped plugin-control experiences and must not obscure the file workflow. - -## Risks / Trade-offs - -- [A config document may contain extra keys] → preserve them in the proposed content and show them as read-only, unmodeled rows. -- [Existing installations still link old route keys] → normalize those keys in the common resolver before page lookup. -- [A declared log stream is absent] → show an explicit unavailable state and never fall back to a filesystem path. -- [INI parsing has formatting limits] → patch only declared simple key/value fields and rely on Platform preview before approval. -- [A raw file has not completed an authorized read] → show an explicit pending or empty state rather than an invented template or stale file content. -- [A declared file contains a secret-like assignment] → redact its value in the snapshot while retaining the surrounding file structure for operational review. - -## Migration Plan - -1. Ship manifest and Platform declaration validation/projection with the SCUM `files-config` page. -2. Deploy the frontend route normalization and workbench; old SCUM links resolve to `files-config`. -3. Rollback by restoring the previous manifest page declaration; no persisted migration or write protocol needs reversal. - -## Open Questions - -None. The first field catalog uses existing SCUM deployment mappings and can be expanded through manifest declarations later. diff --git a/openspec/changes/add-scum-file-config-workbench/proposal.md b/openspec/changes/add-scum-file-config-workbench/proposal.md deleted file mode 100644 index 53a4268..0000000 --- a/openspec/changes/add-scum-file-config-workbench/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -SCUM operators currently reach separate overview, configuration, and semantic-log surfaces, even though their routine operational work begins with declared files and safe, modeled settings. The default SCUM work surface needs to make configuration actionable without revealing host paths or turning raw text editing into the normal workflow. - -## What Changes - -- Replace the SCUM overview-first experience with a unified `文件与配置` workbench whose default scope is the plugin-declared configuration directory. -- Add plugin-declared logical file directories/files and a modeled configuration-field catalog, including Chinese labels, help, control metadata, constraints, defaults, restart impact, and owning file. -- Reuse Platform-mediated file requests and the existing configuration diff-preview/approval/write flow so modeled changes are previewed before dispatch; expose completed declared-file reads through a bounded, redacted Platform snapshot rather than through host paths or general job payloads. -- Expose logs as declared log files inside the same workbench, with separate safe log-file scope; retain unknown configuration fields as read-only information. -- Replace the static, stacked SCUM panels with a single selection-based file workbench: directory/file navigation on one side and the selected file's modeled configuration, optional raw config mode, or read-only log content on the other. -- Embed the workbench inside the server detail section without a second plugin-page header or unrelated Companion feature panels. -- Safely migrate legacy SCUM overview/config/log routes to the new workbench. - -## Capabilities - -### New Capabilities -- `scum-file-config-workbench`: Declared SCUM file scopes, modeled configuration editing, log-file viewing, and safe route migration. - -### Modified Capabilities -- `config-write-and-file-dispatch`: File dispatch and config approval now consume declared logical directories/files and field ownership metadata. -- `scum-operations`: SCUM's primary operator surface changes from an overview to the file-and-configuration workbench. - -## Impact - -- Affects the SCUM plugin manifest/declarations, Platform plugin validation and safe file/config DTO handling, and SCUM frontend contracts/routes/components. -- Reuses `files.request`, existing server configuration diff/approval/write APIs, and the SCUM runtime profile; adds a Platform-owned read-result projection without expanding filesystem or remote-access authority. diff --git a/openspec/changes/add-scum-file-config-workbench/specs/config-write-and-file-dispatch/spec.md b/openspec/changes/add-scum-file-config-workbench/specs/config-write-and-file-dispatch/spec.md deleted file mode 100644 index 1fd3968..0000000 --- a/openspec/changes/add-scum-file-config-workbench/specs/config-write-and-file-dispatch/spec.md +++ /dev/null @@ -1,8 +0,0 @@ -## ADDED Requirements - -### Requirement: Declared field ownership constrains config editing -Platform-projected plugin declarations SHALL associate each modeled configuration field with a declared logical file key so the frontend can invoke existing diff and approval APIs without accepting arbitrary file targets. - -#### Scenario: Preview uses an owning declared config file -- **WHEN** a modeled field change is previewed -- **THEN** the frontend MUST use the field's declared logical file key and MUST NOT accept a host path or user-supplied target key diff --git a/openspec/changes/add-scum-file-config-workbench/specs/scum-file-config-workbench/spec.md b/openspec/changes/add-scum-file-config-workbench/specs/scum-file-config-workbench/spec.md deleted file mode 100644 index 5de0f11..0000000 --- a/openspec/changes/add-scum-file-config-workbench/specs/scum-file-config-workbench/spec.md +++ /dev/null @@ -1,81 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM declares safe file workspace catalog -The SCUM plugin SHALL declare logical configuration and log directories/files and a bounded modeled configuration-field catalog without raw host paths, secrets, credentials, sockets, or arbitrary schemas. - -#### Scenario: Default configuration scope is declared -- **WHEN** an authorized operator opens the SCUM file-and-configuration page -- **THEN** the page MUST select the plugin-declared configuration directory and list only its declared logical files - -#### Scenario: Unsafe declaration is rejected -- **WHEN** a plugin manifest includes an absolute path, traversal key, secret-like value, or unsupported field control -- **THEN** Platform MUST reject the manifest before it is registered - -### Requirement: Modeled SCUM fields are configuration-first -The SCUM workbench SHALL render modeled fields with Chinese label, explanation, input type, constraint, default, restart impact, and owning file, and SHALL keep unmodeled fields read-only. - -#### Scenario: Operator changes a modeled field -- **WHEN** an operator changes a declared editable field -- **THEN** the workbench MUST compose only that field into the owning configuration file and require a diff preview before approval - -#### Scenario: Unknown configuration is encountered -- **WHEN** the loaded configuration includes a key outside the declared field catalog -- **THEN** the workbench MUST show it as read-only and MUST NOT present it as a safe editable control - -### Requirement: Logs are file scope in the same workbench -The SCUM workbench SHALL present plugin-declared log files as a log-file scope and display their Platform-mediated stream content. - -#### Scenario: Operator opens a declared log file -- **WHEN** an operator selects a declared SCUM log file -- **THEN** the page MUST query only its declared Platform log stream and show readable file content without host-path disclosure - -### Requirement: Raw file content is a bounded declared-file snapshot -Platform SHALL expose raw configuration or log text only from the latest completed `files.read` result for the same server and a plugin-declared logical file key. - -#### Scenario: Declared file result is ready -- **WHEN** an authorized operator requests the raw view of a declared file after its read job completes -- **THEN** Platform MUST return only that file's bounded content, logical key, version, checksum, and read state -- **AND THEN** the response MUST NOT contain a host path, a generic job execution payload, or another file's content - -#### Scenario: Declared file is not read yet -- **WHEN** no successful declared-file read is available for the selected file -- **THEN** Platform MUST return an explicit pending or not-read state without inventing file contents - -#### Scenario: Raw file contains a secret-like assignment -- **WHEN** a completed declared-file result includes a secret-like `key=value` assignment -- **THEN** Platform MUST redact the assignment value before returning the raw snapshot - -### Requirement: SCUM workbench has one active file surface -The SCUM workbench SHALL keep declared directory/file navigation separate from the selected file content, rather than stacking every declared file and every unrelated SCUM feature on one page. - -#### Scenario: Operator selects a declared configuration file -- **WHEN** an operator selects `ServerSettings.ini` or another declared configuration file -- **THEN** the workbench MUST show only that file's metadata and supported modes in the content pane -- **AND THEN** modeled fields MUST be limited to fields owned by that selected file - -#### Scenario: Operator switches a selected configuration file to raw mode -- **WHEN** an operator activates the raw configuration mode for a selected declared configuration file -- **THEN** the workbench MUST show only the most recent Platform-mediated file result or an explicit not-yet-read state -- **AND THEN** an editable declared configuration file MAY expose a raw editor only after a completed read snapshot is available -- **AND THEN** raw-mode changes MUST require a visible diff preview before dispatching a declared logical `files.write` request -- **AND THEN** it MUST NOT expose a host path, arbitrary file selector, or unrestricted text editor - -#### Scenario: Operator selects a declared log file -- **WHEN** an operator selects a declared log file -- **THEN** the workbench MUST replace the configuration controls with that log file's declared read surface -- **AND THEN** the log raw view MUST stay read-only and support switching between UTF-8 and UTF-16 LE display - -### Requirement: Embedded SCUM file management avoids duplicate page chrome -When the SCUM workbench is rendered inside a server detail section, the frontend SHALL render it without a second plugin page frame, host-context panel, or unrelated Companion feature panels. - -#### Scenario: Server detail opens SCUM file management -- **WHEN** an operator opens the SCUM `文件管理` section in server detail -- **THEN** the first workbench surface MUST be the declared directory/file navigation and selected file content -- **AND THEN** the page MUST NOT render a nested `PLUGIN PAGE` title or `平台托管上下文` panel - -### Requirement: Legacy SCUM pages migrate safely -The frontend SHALL migrate legacy SCUM overview, config, logs, and operations page keys to the `files-config` page while leaving non-SCUM routing unchanged. - -#### Scenario: Legacy SCUM operations link is opened -- **WHEN** a user opens a SCUM plugin URL with the legacy `operations` key -- **THEN** the frontend MUST resolve it to the declared `files-config` workbench for the same server context diff --git a/openspec/changes/add-scum-file-config-workbench/specs/scum-operations/spec.md b/openspec/changes/add-scum-file-config-workbench/specs/scum-operations/spec.md deleted file mode 100644 index 4ffc0b2..0000000 --- a/openspec/changes/add-scum-file-config-workbench/specs/scum-operations/spec.md +++ /dev/null @@ -1,8 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM primary surface is file and configuration workbench -The SCUM plugin SHALL expose `files-config` as its primary operator page rather than an overview-first or semantic-log-dashboard page. - -#### Scenario: SCUM plugin page opens -- **WHEN** a user opens the declared primary SCUM page for a server instance -- **THEN** the page MUST prioritize declared configuration files and modeled configuration controls before log-file browsing diff --git a/openspec/changes/add-scum-file-config-workbench/tasks.md b/openspec/changes/add-scum-file-config-workbench/tasks.md deleted file mode 100644 index dd1b587..0000000 --- a/openspec/changes/add-scum-file-config-workbench/tasks.md +++ /dev/null @@ -1,18 +0,0 @@ -## 1. Plugin and Platform declaration contract - -- [x] 1.1 Add safe logical file-directory/file and modeled-field contracts to plugin manifests, Platform domain/DTO projections, copying, and validation. -- [x] 1.2 Declare SCUM configuration/log files and bounded Chinese configuration field metadata, then remove legacy SCUM overview page declarations. -- [x] 1.3 Add focused Platform validation/projection tests for safe declaration and field ownership behavior. -- [x] 1.4 Add a bounded redacted Platform projection for completed declared-file read results. - -## 2. SCUM file and configuration workbench - -- [x] 2.1 Add frontend declaration contracts and resolver for the SCUM `files-config` default workbench and legacy route migration. -- [x] 2.2 Build the shared-theme selection-based file list, selected-file modeled configuration form, raw config mode, unknown-field list, preview diff, and declared write dispatch experience. -- [x] 2.3 Add declared log-file scope and Platform log stream content reading without path exposure. -- [x] 2.4 Render the SCUM workbench embedded in server detail and keep unrelated Companion feature panels out of the file-management tab. - -## 3. Verification - -- [x] 3.1 Add/update focused backend, plugin, route, contract, and component tests for selection state, raw mode, and embedded rendering. -- [x] 3.2 Run strict OpenSpec validation, relevant backend/plugin/frontend checks, browser verification, and scripts/check-structure.sh. diff --git a/openspec/changes/add-scum-game-player-intelligence/.openspec.yaml b/openspec/changes/add-scum-game-player-intelligence/.openspec.yaml deleted file mode 100644 index e8209ff..0000000 --- a/openspec/changes/add-scum-game-player-intelligence/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-28 diff --git a/openspec/changes/add-scum-game-player-intelligence/design.md b/openspec/changes/add-scum-game-player-intelligence/design.md deleted file mode 100644 index 87694f7..0000000 --- a/openspec/changes/add-scum-game-player-intelligence/design.md +++ /dev/null @@ -1,57 +0,0 @@ -## Context - -Platform already receives sequenced, durable runtime log batches and has a `game.scum` manifest that declares `scum.login` and `scum.logout` schemas. Those entries are queryable as logs but are not durable player-domain records. The new projection must remain local to a server instance, be safe to invoke as part of log ingestion, and not make raw network data browser-visible or persistent. - -## Goals / Non-Goals - -**Goals:** - -- Persist independent game-player records keyed by server instance and SCUM player ID, with aliases, bounded sessions, access attempts, and review-only security signals. -- Project success login/logout semantics atomically and idempotently from accepted log entries, with sequence/event keys for duplicates and timestamp ordering for stale updates. -- Derive server-isolated correlation identifiers using an HMAC secret and a normalized source value, while retaining only the derived value. -- Offer permission-scoped, server-authorized management APIs and console records that never return IP values, raw source identifiers, secrets, raw log lines, or automatic enforcement controls. -- Retain access attempts and signals for a configurable bounded period and preserve player identity/session summaries after evidence expiry. - -**Non-Goals:** - -- Platform-user accounts, credentials, game database reads/writes, skill/attribute editing, gifts, map trails, or any automatic ban/kick/punishment. -- Changes to Run channels, direct host/socket/database access, or browser access to raw semantic log bodies. - -## Decisions - -1. **Use server-local game identity.** `game_player` is unique on `(server_instance_id, game_player_id)`; it has no foreign key to `user`. This prevents platform-console authentication from being mistaken for SCUM identity. An alias table records renamed display names with first/last-seen times. - - Alternative considered: use display name as the identifier. It cannot safely handle player renames or duplicate names. - -2. **Project only accepted SCUM semantic events in the ingestion transaction.** The log sequence (`log_stream_id`, `seq`) is the projection idempotency key. Login/open-session changes only apply when their event time is not older than the player's latest projected event; a logout closes the latest matching open session no later than its event time. Duplicate batches perform no second projection. - - Alternative considered: asynchronous parsing of raw log lines. It would duplicate declared parsing rules, complicate order guarantees, and retain unnecessary sensitive content. - -3. **Store only an HMAC-derived, server-scoped network correlation key.** A server-specific HMAC domain separator plus a process secret produces the correlation key. The source is never saved in models, logs, DTOs, or signal evidence. A digest is useful only within the same server and cannot be compared across servers. - - Alternative considered: hash the raw IP directly. Unsalted hashes are reversible for common address spaces and correlatable across servers. - -4. **Treat failures and anomalies as review evidence.** Failed login/connection event payloads create bounded `game_access_attempt` records. A threshold (five failures for the same fingerprint within fifteen minutes) creates or refreshes an `excessive-failed-access` signal; multiple player IDs sharing one server-local fingerprint create a `possible-alt-account` signal. Signals include text status and evidence counts, and have no action API. - - Alternative considered: automatic moderation. It is excluded because heuristic evidence requires an operator review. - -5. **Use existing session authorization and console primitives.** New endpoints use existing server read authorization. The frontend fetches named API contracts and renders an ordinary full-width shared table/record list with explicit status labels, never raw logs or a new page-local visual system. - -## Risks / Trade-offs - -- [A plugin payload omits a usable player ID] → Ignore the event for projection and retain normal log ingestion; validate only bounded known fields. -- [Late logout arrives after another login] → Close only a compatible open session whose start is no later than logout; never regress `lastSeenAt`. -- [Projection failure follows log body append] → Return the ingest error and retry the acknowledged range safely; event identity prevents duplicate records. -- [HMAC secret rotates] → Derived keys intentionally cease correlating across rotations; raw data is never recoverable. Deployment config keeps the secret stable during its intended retention window. -- [Evidence tables grow] → Perform retention pruning during projection/query and cap query limits. - -## Migration Plan - -1. Add model-first tables and repositories; existing installations begin with no projected players. -2. Deploy manifest schemas and Platform projection; only newly ingested declared events create records. Historical log backfill remains an explicit later operation. -3. Deploy APIs and console page after backend authorization is available. -4. Rollback by disabling the page and projection. Existing records contain no raw network data and can expire through normal retention cleanup. - -## Open Questions - -None. Initial thresholds and retention are conservative constants covered by tests and can become declared policy in a later change. diff --git a/openspec/changes/add-scum-game-player-intelligence/proposal.md b/openspec/changes/add-scum-game-player-intelligence/proposal.md deleted file mode 100644 index 661c4a5..0000000 --- a/openspec/changes/add-scum-game-player-intelligence/proposal.md +++ /dev/null @@ -1,23 +0,0 @@ -## Why - -SCUM login semantics are currently retained only as operational logs, so operators cannot safely review a local player's identity history, sessions, or suspicious access behavior. A server-scoped game-player projection turns declared SCUM login/logout events into reviewable evidence without conflating game identities with platform users or exposing network data. - -## What Changes - -- Add a server-scoped local game-player domain with aliases, sessions, access attempts, and review-only security signals. -- Project declared `scum.login` and `scum.logout` semantic log events idempotently, including duplicate, renamed-player, out-of-order, and session-boundary handling. -- Derive a server-isolated irreversible network correlation fingerprint; do not persist raw IP addresses or raw fingerprints. -- Add authenticated player-profile, aliases, access-trajectory, and security-signal APIs plus SCUM console contracts and records. -- Define bounded retention and manual-review semantics for access evidence and risk signals. - -## Capabilities - -### New Capabilities - -- `scum-game-player-intelligence`: Server-local SCUM player identities, event projection, privacy-preserving access evidence, and review-only risk signals. - -## Impact - -- Affects Platform domain/model/repository/service/API/validation layers and durable metadata storage. -- Affects the SCUM plugin semantic-event schemas and page declaration, plus Platform Web API contracts, routing, and shared-console records. -- Adds no Platform account authentication, direct game-database access, raw network data, automatic moderation, Run protocol, or host/executor data exposure. diff --git a/openspec/changes/add-scum-game-player-intelligence/specs/scum-game-player-intelligence/spec.md b/openspec/changes/add-scum-game-player-intelligence/specs/scum-game-player-intelligence/spec.md deleted file mode 100644 index 1eb56f3..0000000 --- a/openspec/changes/add-scum-game-player-intelligence/specs/scum-game-player-intelligence/spec.md +++ /dev/null @@ -1,51 +0,0 @@ -## ADDED Requirements - -### Requirement: Server-local game-player identity -The system SHALL maintain a game-player identity independently of platform users, uniquely scoped by `serverInstanceId` and immutable game-player ID, with bounded display-name alias history. - -#### Scenario: A known game player logs in under a new name -- **WHEN** a successful declared SCUM login contains an existing player ID and a different valid display name -- **THEN** the system SHALL update the player's current display name and retain the previous and new names as server-local aliases - -### Requirement: Idempotent semantic login/logout projection -The system SHALL project accepted `scum.login` and `scum.logout` semantic events using a durable event identity and SHALL tolerate duplicate and out-of-order delivery without creating duplicate access evidence or regressing player state. - -#### Scenario: A duplicate login event is received -- **WHEN** the same accepted log stream sequence is ingested again -- **THEN** the system SHALL return the normal duplicate acknowledgement and SHALL not create an additional session or alias record - -#### Scenario: A stale logout arrives after a later login -- **WHEN** a logout event is older than the player's latest successful login -- **THEN** the system SHALL only close an eligible earlier open session and SHALL not overwrite the newer player last-seen state - -### Requirement: Privacy-preserving access evidence -The system SHALL persist no raw IP address, raw network fingerprint, host path, credential, or raw sensitive log content in game-player access records or browser-visible responses. Network correlation SHALL be derived as an irreversible value scoped to one server instance. - -#### Scenario: An access event carries a source address -- **WHEN** a SCUM event contains a source address or network identifier -- **THEN** the system SHALL use it only to derive a server-isolated correlation value and SHALL omit the source value from stored models and API responses - -### Requirement: Review-only risk signals -The system SHALL create bounded security signals for thresholded failed access and possible shared-fingerprint identities, and SHALL expose them as manual-review evidence only. - -#### Scenario: Repeated failed access crosses the threshold -- **WHEN** five failed attempts with one server-local correlation value occur within fifteen minutes -- **THEN** the system SHALL create or refresh an excessive-failed-access signal with an explicit textual review status and evidence count - -#### Scenario: An operator reviews a signal -- **WHEN** an authorized operator reads a player security signal -- **THEN** the response SHALL contain no automated enforcement command, raw network identifier, or raw log line - -### Requirement: Authorized player intelligence console -The system SHALL provide server-authorized player profile, aliases, sessions, access-attempt, and security-signal responses through named contracts, and the SCUM console SHALL render labels and status text rather than relying only on color. - -#### Scenario: An unauthorized user requests server player data -- **WHEN** a platform session lacks read access to the requested server instance -- **THEN** the player intelligence API SHALL deny access without revealing whether a game-player record exists - -### Requirement: Bounded evidence retention -The system SHALL retain access attempts and active security evidence for a bounded period, prune expired evidence during normal service operations, and keep independent player identity history intact. - -#### Scenario: Evidence is older than the retention limit -- **WHEN** a query or projection runs after an access attempt exceeds the retention period -- **THEN** the system SHALL remove the expired attempt and any expired non-active signal evidence without deleting the game-player identity diff --git a/openspec/changes/add-scum-game-player-intelligence/tasks.md b/openspec/changes/add-scum-game-player-intelligence/tasks.md deleted file mode 100644 index 32fb332..0000000 --- a/openspec/changes/add-scum-game-player-intelligence/tasks.md +++ /dev/null @@ -1,21 +0,0 @@ -## 1. Domain and persistence - -- [x] 1.1 Add server-local game-player, alias, session, access-attempt, and security-signal domain/models, repositories, MySQL migration, memory/file stores, validation, and retention helpers. -- [x] 1.2 Add idempotent SCUM login/logout and failed-access projection from accepted semantic log entries, including rename, duplicate, stale event, session boundary, isolated HMAC correlation, and review-only signal thresholds. -- [x] 1.3 Add focused domain/service/repository tests for privacy, ordering, idempotency, access control, retention, aliases, sessions, and risk signals. - -## 2. APIs and plugin contract - -- [x] 2.1 Add named request/response DTOs, authorized handlers/routes, safe projections, and API tests for player profiles, lists, aliases, sessions, attempts, and signals. -- [x] 2.2 Extend the SCUM manifest semantic schemas/page contract for bounded player intelligence and add plugin validation fixtures/tests. - -## 3. SCUM console - -- [x] 3.1 Add Platform Web API/client contracts and route resolution for the SCUM player intelligence work surface. -- [x] 3.2 Render full-width shared-console player records, access trajectories, and manual-review signals with explicit text status and no raw network/log exposure. -- [x] 3.3 Add frontend contract/component tests for safe rendering and status readability. - -## 4. Verification - -- [x] 4.1 Run strict OpenSpec validation and focused backend, plugin, and frontend test suites. -- [x] 4.2 Run `scripts/check-structure.sh`, stage only task files, commit on `main`, and push the configured remote. diff --git a/openspec/changes/add-scum-map-trajectories/.openspec.yaml b/openspec/changes/add-scum-map-trajectories/.openspec.yaml deleted file mode 100644 index e8209ff..0000000 --- a/openspec/changes/add-scum-map-trajectories/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-28 diff --git a/openspec/changes/add-scum-map-trajectories/README.md b/openspec/changes/add-scum-map-trajectories/README.md deleted file mode 100644 index 13f291d..0000000 --- a/openspec/changes/add-scum-map-trajectories/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# add-scum-map-trajectories - -SCUM player and vehicle map trajectory projection diff --git a/openspec/changes/add-scum-map-trajectories/design.md b/openspec/changes/add-scum-map-trajectories/design.md deleted file mode 100644 index d9006a7..0000000 --- a/openspec/changes/add-scum-map-trajectories/design.md +++ /dev/null @@ -1,45 +0,0 @@ -## Context - -The existing SCUM player intelligence projection owns server-local identities and sessions, while the game-client bridge owns declared Companion snapshots and commands. Neither layer presents movement. The map must remain an explainable, bounded operational view and may not turn the browser into a route to raw logs, Run, game databases, or Companion sockets. - -## Goals / Non-Goals - -**Goals:** - -- Collect only declared semantic position/vehicle transition events from a platform-managed Companion or accepted log projection. -- Convert plugin-declared world coordinates into safe normalized map coordinates and retain only a sampled, bounded trail per server/entity. -- Return a fixed bounded window with player/vehicle filters, vehicle-riding segments, data source, collection time, map version, and precision. -- Enforce existing server authorization before resolving map status or trajectory data; preserve server isolation. -- Render readable point and line trails with explicit empty and missing-map states and detail navigation contracts. - -**Non-Goals:** - -- OCR, screenshot parsing, mouse/keyboard injection, desktop automation, direct game-window access, direct sockets, direct database access, raw coordinate/database querying, indefinite retention, real-time all-server heatmaps, or raw log/browser delivery. - -## Decisions - -1. **A small typed event catalog is the only collector contract.** The SCUM manifest declares four semantic events: `player.position`, `vehicle.position`, `player.vehicle.enter`, and `player.vehicle.leave`. Every event includes a bounded entity ID, occurred time, collection time, source (`companion` or `log-projection`), and a plugin map ID/version. Position events carry only finite world X/Y values. They enter through the existing durable log/Companion snapshot channel, never browser-to-game traffic. - -2. **Plugin map declaration defines the safe projection.** `mapTrajectories` in the manifest declares map ID/version, world bounds, image dimensions, axis orientation, sampling distance/time, and retention seconds. Platform validates it once, converts world coordinates to normalized 0–1000 map units, rounds to declared precision, and rejects mismatched map metadata or points outside declared bounds. The frontend receives no host path, original world coordinate, Companion endpoint, or raw record body. - -3. **Project by server/entity/time with event identity and compression.** `GameMapTrackPoint` is unique by accepted event ID and is scoped by server plus entity kind/ID. Projection sorts logically by event time; duplicates are ignored, late points remain ordered in query results, and a point is retained only when it advances the declared sampling interval or distance (transitions are always retained). The repository filters time on server/entity indexes and prunes expired points and ride segments during projection and query. - -4. **Derive riding intervals from enter/leave events.** A player enter opens one vehicle segment; entering a different vehicle closes the prior segment at the new event time. A leave closes only the matching active vehicle. Out-of-order and duplicate transitions cannot produce overlapping active segments. The map response projects these segments as player-associated vehicle trail intervals, not inferred ownership. - -5. **Use a bounded read model instead of arbitrary map queries.** The API accepts a maximum 24-hour time window and at most 20 known player IDs plus 20 known vehicle IDs. It authorizes server access before lookup, verifies player IDs belong to that server, limits output points per entity, and returns map metadata plus empty/missing-map statuses. Vehicle IDs are only accepted if observed in the same server's declared vehicle snapshots or trajectory records. - -6. **Keep detail linking as explicit identifiers.** Player trail summaries provide the existing `gamePlayerRecordId`; vehicles provide their safe vehicle ID and link intent. The frontend may navigate to the existing player selection/detail endpoint or invoke the pre-existing vehicle lookup context. It does not receive a route to Run, a raw query template, or vehicle storage details. - -## Risks / Trade-offs - -- [Companion cannot provide typed position events] → the map shows a clear collection-unavailable/empty state; no substitute screen or input automation is attempted. -- [Map version changes] → points are queried only for the declared map version; mismatched events are rejected and the response explains the missing compatible map. -- [High event volume] → server-side sampling, per-entity output caps, and retention pruning bound storage and response size. -- [Late events] → query sorting and identity deduplication keep trails deterministic; transition rules avoid reopened or cross-vehicle overlap. - -## Migration Plan - -1. Add model-first track/segment records, memory/file/MySQL persistence, projection, retention, and test coverage. -2. Register the plugin map declaration and typed schemas; unsupported deployments remain visibly empty. -3. Deploy the authorized API and console map. Existing player and vehicle views remain unchanged. -4. Roll back by removing the map panel/declaration; expired projected records are pruned normally and no raw data needs migration. diff --git a/openspec/changes/add-scum-map-trajectories/proposal.md b/openspec/changes/add-scum-map-trajectories/proposal.md deleted file mode 100644 index 0b30e8b..0000000 --- a/openspec/changes/add-scum-map-trajectories/proposal.md +++ /dev/null @@ -1,23 +0,0 @@ -## Why - -SCUM operations currently show player identity and vehicle snapshots, but operators cannot explain where a selected player or vehicle has been over a bounded period. A server-scoped, permission-checked map projection is needed without exposing raw logs, Companion connectivity, host data, or arbitrary coordinate queries. - -## What Changes - -- Define controlled SCUM Companion/log-projection events for `player.position`, `vehicle.position`, `player.vehicle.enter`, and `player.vehicle.leave`. -- Require the plugin to declare map version, coordinate conversion, sampling precision, and retention policy; project accepted events into server-isolated trajectory records. -- Add a bounded, authorized map API with time windows, player/vehicle filters, vehicle-riding segments, source/collection timestamps, and explicit missing-map/empty states. -- Add a Chinese SCUM console map view with point-and-line trajectories and links to the existing player detail and vehicle context. - -## Capabilities - -### New Capabilities - -- `scum-map-trajectories`: Controlled collection, projection, retention, authorization, and map rendering of SCUM player and vehicle trajectories. - -## Impact - -- Affects platform domain/model/repository/service/validation/API DTOs and durable metadata snapshots. -- Extends the SCUM plugin manifest and schemas with bounded map metadata and semantic trajectory events. -- Adds platform-web API contracts and a shared-console map surface; it receives only safe projected coordinates and metadata. -- Does not add OCR, screen/input automation, direct game/window access, raw logs, unbounded location retention, or live heatmaps. diff --git a/openspec/changes/add-scum-map-trajectories/specs/scum-map-trajectories/spec.md b/openspec/changes/add-scum-map-trajectories/specs/scum-map-trajectories/spec.md deleted file mode 100644 index 385e6fe..0000000 --- a/openspec/changes/add-scum-map-trajectories/specs/scum-map-trajectories/spec.md +++ /dev/null @@ -1,59 +0,0 @@ -## ADDED Requirements - -### Requirement: Controlled SCUM trajectory event contracts -The system SHALL accept SCUM location information only as platform-managed Companion or accepted log-projection semantic events named `player.position`, `vehicle.position`, `player.vehicle.enter`, and `player.vehicle.leave`, each carrying bounded IDs, occurrence and collection timestamps, declared source, and matching map metadata. - -#### Scenario: An undeclared or malformed location event arrives -- **WHEN** an event has an unknown type, malformed bounded ID, non-finite coordinate, invalid source, or mismatched map declaration -- **THEN** the system SHALL reject it from trajectory projection and SHALL not expose raw event material to the browser - -### Requirement: Plugin-declared safe map conversion -The SCUM plugin SHALL declare a map ID/version, coordinate transform/world bounds, sampling precision, and finite retention period. The platform SHALL convert accepted world points into a rounded safe map projection before persistence or API delivery. - -#### Scenario: A point is outside declared map bounds -- **WHEN** a valid position event has world coordinates outside the declared transform bounds -- **THEN** the platform SHALL not persist or return a map point for that event - -#### Scenario: Map metadata is absent -- **WHEN** a server plugin has no compatible map declaration -- **THEN** the map API and console SHALL return a readable missing-map state without attempting alternative collection - -### Requirement: Ordered, sampled, and retained server-isolated trajectories -The system SHALL index points by server, entity, and occurrence time; tolerate duplicate and out-of-order events; apply declared sampling compression; and remove points and closed ride segments after the declared retention period. - -#### Scenario: Duplicate or delayed position event -- **WHEN** the same accepted event is delivered twice or an older point arrives after a newer point -- **THEN** the system SHALL retain no duplicate and SHALL return all accepted points ordered by occurrence time without regressing current sampling state - -#### Scenario: A point is below the sampling threshold -- **WHEN** a same-entity point occurs inside the declared minimum time and distance thresholds -- **THEN** the system SHALL compress it rather than persist another display point - -#### Scenario: Retention expires -- **WHEN** normal projection or map retrieval occurs after a point or closed riding segment passes its retention deadline -- **THEN** the system SHALL remove the expired record while preserving unrelated player identity and vehicle snapshot data - -### Requirement: Vehicle ride association -The system SHALL derive player vehicle segments from typed enter/leave events and SHALL close a prior active segment before opening a segment for a different vehicle. - -#### Scenario: A player changes vehicles without a leave event -- **WHEN** a player enters a second vehicle while a first vehicle segment remains active -- **THEN** the system SHALL close the first segment at the second enter time and open one segment for the second vehicle - -### Requirement: Authorized bounded map read model -The system SHALL authorize server access before returning a maximum 24-hour trajectory window and SHALL expose only safe projected points, declared map metadata, collection/source labels, entity summaries, and detail-link identifiers. It SHALL not expose raw coordinates, raw logs, IPs, paths, credentials, host information, or Run/Companion connectivity. - -#### Scenario: Unauthorized map request -- **WHEN** a session lacks access to the requested server instance -- **THEN** the API SHALL deny the request without revealing map availability, entity existence, or trajectory data - -#### Scenario: Cross-server entity selector -- **WHEN** a requested player or vehicle ID belongs only to another server -- **THEN** the response SHALL not include its points or reveal the other server association - -### Requirement: Explainable SCUM map console -The SCUM console SHALL render declared map metadata, collection source/times, selected player and vehicle points/lines, ride segments, time and entity filters, and textual empty or missing-map states. Map entity interactions SHALL use the returned safe detail-link identifiers. - -#### Scenario: An operator selects a trail entity -- **WHEN** an operator selects a player or vehicle map item -- **THEN** the console SHALL navigate or invoke the matching player/vehicle detail context using the returned identifier without constructing arbitrary coordinate or data queries diff --git a/openspec/changes/add-scum-map-trajectories/tasks.md b/openspec/changes/add-scum-map-trajectories/tasks.md deleted file mode 100644 index 0a1a30b..0000000 --- a/openspec/changes/add-scum-map-trajectories/tasks.md +++ /dev/null @@ -1,19 +0,0 @@ -## 1. Specification and contracts - -- [x] 1.1 Define the platform domain/model/repository contracts for declared map metadata, safe points, ride segments, bounded filters, and map read response. -- [x] 1.2 Add SCUM manifest map declaration and typed semantic event schemas for position and vehicle transitions. - -## 2. Platform projection and API - -- [x] 2.1 Implement map declaration validation, safe coordinate conversion, sampling compression, event deduplication/order handling, ride-segment projection, and expiry pruning. -- [x] 2.2 Add authorized server map route, named DTOs, and safe response mapping with time/entity bounds and missing-map state. -- [x] 2.3 Add backend tests for conversion, duplicate/out-of-order events, cross-vehicle segments, filters, authorization, sampling, retention, and cross-server isolation. - -## 3. Console - -- [x] 3.1 Add frontend API/types/contracts and a shared-theme SCUM map component with time/player/vehicle filters, point/line trails, source/timestamp labels, and empty/missing-map states. -- [x] 3.2 Wire safe player/vehicle detail interactions and add focused component/schema tests. - -## 4. Verification - -- [x] 4.1 Run relevant Go, plugin, and frontend tests/build; run `openspec validate add-scum-map-trajectories --strict` and `scripts/check-structure.sh`. diff --git a/openspec/changes/add-scum-player-state-patch/.openspec.yaml b/openspec/changes/add-scum-player-state-patch/.openspec.yaml deleted file mode 100644 index 4cb23b4..0000000 --- a/openspec/changes/add-scum-player-state-patch/.openspec.yaml +++ /dev/null @@ -1,4 +0,0 @@ -schema: spec-driven -created: 2026-07-28 -goal: Provide safe authorized SCUM player attribute updates through the declared - game-client bridge. diff --git a/openspec/changes/add-scum-player-state-patch/README.md b/openspec/changes/add-scum-player-state-patch/README.md deleted file mode 100644 index 4bd21ba..0000000 --- a/openspec/changes/add-scum-player-state-patch/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# add-scum-player-state-patch - -SCUM player detail and approved version-scoped attribute patching diff --git a/openspec/changes/add-scum-player-state-patch/design.md b/openspec/changes/add-scum-player-state-patch/design.md deleted file mode 100644 index ef01f96..0000000 --- a/openspec/changes/add-scum-player-state-patch/design.md +++ /dev/null @@ -1,42 +0,0 @@ -## Context - -The prior `scum-game-player-intelligence` change owns server-local player identity, sessions, risk signals, and access control. The existing game-client bridge supplies declared commands, approval states, leases, audit references, and result fencing, but it does not itself constrain individual game-state fields or maintain a readable player-change history. - -## Goals / Non-Goals - -**Goals:** - -- Permit only catalogued SCUM skills and character attributes for an explicitly declared server game version. -- Verify player/server ownership, current state version, companion availability, declared maintenance/online safety state, and platform-admin approval before dispatch. -- Record before/after value, reason, requester, approver, bridge command result, and read-after-write confirmation as an immutable patch audit. -- Prevent unknown versions, fields, out-of-range values, stale versions, unsafe execution windows, and unknown/failed execution from being represented as applied. - -**Non-Goals:** - -- SQL or database access, raw JSON/INI write APIs, host paths, direct Run/game connections, OCR/input automation, player identity/risk-signal edits, bans, gifts, or map trails. - -## Decisions - -1. **Catalog at the platform boundary.** `SCUMPlayerStateCatalog` maps a declared server game version to a small list of field keys and numeric ranges. No caller or plugin payload can introduce fields dynamically. The initial exact version is `0.9.700.90357` and supports `skills.running` and `attributes.strength` in range 0–10. - -2. **Use a dedicated typed bridge operation.** The manifest declares only `game-state.patch`, with a schema requiring target player ID, game version, expected state version, safety-window token, reason, and a list of catalogued changes. The result carries a bounded per-field outcome and an immediate confirmed state version; it contains no raw storage/database material. - -3. **Two-stage platform-admin approval.** A server-authorized requester creates a durable patch record in `pending-approval`. A platform admin approves it after revalidating authorization, catalog, snapshot version, and safety window. Approval queues the bridge command and remains auditable; a requester who is also platform admin still creates then approves the explicit record. - -4. **Snapshot/state fencing and confirmation.** A current `player.state` snapshot is the source of before values and its `stateVersion` is copied into the requested patch. The companion must reject mismatches, apply only declared fields in a verified maintenance/offline safety window, then read state back before returning success. Platform marks a record `confirmed` only when each returned value exactly equals its requested value and the returned state version advances. Failed and unknown results stay readable terminal audit states. - -5. **Ownership is revalidated at every transition.** Player record lookup checks server ownership before viewing, creating, approving, or reading a patch. The bridge command is scoped to the same server plugin and profile; the generic queue cannot be used as an alternate raw patch entry point because the dedicated service owns field/snapshot validation and records the audit before dispatch. - -## Risks / Trade-offs - -- [Companion has no exact version/state snapshot] → Disable the form and return a readable unsupported/unknown-version result. -- [State changes between snapshot and execution] → The expected state version causes the companion to reject; record the conflict without claiming application. -- [Companion times out or result is missing] → Keep the record `execution-unknown`; do not infer a write, and require a later confirmation read. -- [Maintenance state becomes unsafe] → Approval and companion both reject dispatch; no write is attempted. - -## Migration Plan - -1. Add model-first patch records, memory/file/MySQL repositories, and immutable transition helpers. -2. Deploy the manifest schemas and companion version declaration; no version is implicitly supported. -3. Deploy the API and console. Existing player profiles retain all read-only intelligence behavior. -4. Roll back by disabling the command declaration and form; historical audit records remain readable. diff --git a/openspec/changes/add-scum-player-state-patch/proposal.md b/openspec/changes/add-scum-player-state-patch/proposal.md deleted file mode 100644 index 2b22e65..0000000 --- a/openspec/changes/add-scum-player-state-patch/proposal.md +++ /dev/null @@ -1,22 +0,0 @@ -## Why - -SCUM player intelligence currently presents useful local identity and risk context, but administrators cannot safely correct the limited in-game state that the installed server version explicitly supports. A controlled patch workflow is required so changes remain reviewable, version-fenced, approved, and executed only through the companion command channel. - -## What Changes - -- Add a SCUM version-scoped player-state field catalog for supported skills and character attributes only. -- Add player-state read snapshots and an approved `game-state.patch` bridge operation with optimistic state-version checks, safe execution-window checks, and confirmation reads. -- Persist an immutable patch audit record containing requested old/new values, reason, requester/approver, command result, and confirmation status. -- Add authorized API contracts and Chinese console controls that show the editable diff, approval state, readable outcome, and player context. - -## Capabilities - -### New Capabilities - -- `scum-player-state-patch`: Version-fenced, approved SCUM player skill and character-attribute changes via the game-client bridge. - -## Impact - -- Affects Platform player domain/models/repositories/services/validation/API and durable metadata storage. -- Extends the SCUM manifest with a bounded patch command and typed schemas, plus Platform Web contracts and console records. -- Does not permit SQL, raw configuration/JSON/INI writing, direct game database access, unbounded state editing, automatic moderation, gifts, or map trails. diff --git a/openspec/changes/add-scum-player-state-patch/specs/scum-player-state-patch/spec.md b/openspec/changes/add-scum-player-state-patch/specs/scum-player-state-patch/spec.md deleted file mode 100644 index 4c1d72d..0000000 --- a/openspec/changes/add-scum-player-state-patch/specs/scum-player-state-patch/spec.md +++ /dev/null @@ -1,48 +0,0 @@ -## ADDED Requirements - -### Requirement: Version-scoped player state catalog -The system SHALL allow SCUM player state patches only for a declared exact game version and an explicit catalog of skill/attribute fields with numeric ranges. - -#### Scenario: Unknown server version -- **WHEN** an administrator requests a patch and the current server state reports an unknown game version -- **THEN** the system SHALL disable the patch request and SHALL not queue a bridge command - -#### Scenario: Out-of-range or unknown field -- **WHEN** a request contains a field absent from the version catalog or a value outside its range -- **THEN** the system SHALL reject the request before an audit approval or game-side command is created - -### Requirement: Authorized and safe patch request -The system SHALL ensure that the game player belongs to the target server, the requester has server access, the expected player-state version matches the current snapshot, and the snapshot declares a verified maintenance/offline safety window before creating a patch record. - -#### Scenario: Stale player state -- **WHEN** a patch carries an expected state version different from the current player-state snapshot -- **THEN** the system SHALL reject it as a conflict and SHALL not dispatch a patch command - -#### Scenario: Online server without a safe window -- **WHEN** a state snapshot indicates the target player is online or maintenance is not verified -- **THEN** the system SHALL reject the request with a readable safety status - -### Requirement: Explicit administrator approval and immutable audit -The system SHALL persist each accepted request with field-level before/after values, reason, requester, approval state, approver, and execution status. Only a platform administrator with server access MAY approve dispatch. - -#### Scenario: Non-admin approval -- **WHEN** a server-authorized non-platform-admin attempts to approve a pending patch -- **THEN** the system SHALL deny approval and SHALL leave the patch pending - -### Requirement: Typed game-state patch execution and confirmation -The system SHALL dispatch only the declared `game-state.patch` command through the game-client bridge. A successful record SHALL require a typed per-field result and a read-after-write confirmation whose values equal the requested values and whose state version advances. - -#### Scenario: Companion reports a failure -- **WHEN** the companion returns a failed patch command result -- **THEN** the patch record SHALL be terminal `execution-failed`, preserve the readable result summary, and SHALL not be reported as applied - -#### Scenario: Result is missing or unconfirmable -- **WHEN** the command expires, is cancelled, returns malformed state, or cannot confirm the requested values -- **THEN** the record SHALL retain an explicit `execution-unknown` or `confirmation-failed` status and SHALL not be reported as applied - -### Requirement: Readable SCUM console workflow -The SCUM player console SHALL show player detail context and provide a Chinese patch form only for supported safe states. It SHALL show a textual field diff, reason, approval status, executor result, and confirmation status without relying only on color. - -#### Scenario: Pending approval -- **WHEN** a patch is awaiting approval -- **THEN** the console SHALL label it as awaiting platform-administrator approval and display the old/new values and requester reason diff --git a/openspec/changes/add-scum-player-state-patch/tasks.md b/openspec/changes/add-scum-player-state-patch/tasks.md deleted file mode 100644 index 68297a6..0000000 --- a/openspec/changes/add-scum-player-state-patch/tasks.md +++ /dev/null @@ -1,21 +0,0 @@ -## 1. Contracts, models, and persistence - -- [x] 1.1 Add version-scoped SCUM state catalog, player-state snapshot/patch domain types, audit model/repositories/migration/store implementations, and field/safety validation. -- [x] 1.2 Add service state machine for request, platform-admin approval, bridge-result reconciliation, and confirmation-read semantics with immutable audit evidence. -- [x] 1.3 Add focused backend tests for unknown version, invalid field/range, server ownership, stale version, unsafe window, approval permission, execution failure/unknown, audit, and confirmation. - -## 2. API and SCUM bridge contract - -- [x] 2.1 Add named DTOs and authorized player-state/patch API routes and tests. -- [x] 2.2 Declare `game-state.patch` and player-state schemas in the SCUM manifest, extend companion bridge validation fixtures/tests, and preserve the typed bounded command channel. - -## 3. SCUM console - -- [x] 3.1 Add frontend API/types and Chinese player-detail patch controls using shared console/theme primitives. -- [x] 3.2 Render readable diff, reason, approval, execution, and confirmation histories; disable unsupported or unsafe writes. -- [x] 3.3 Add frontend tests for field labels, diff/audit readability, and disabled safety states. - -## 4. Verification - -- [x] 4.1 Run strict OpenSpec validation, focused backend/plugin/frontend tests, and `scripts/check-structure.sh`. -- [x] 4.2 Stage only this task's files, commit on `main`, and push the configured remote. diff --git a/openspec/changes/add-scum-source-rcon-transport/.openspec.yaml b/openspec/changes/add-scum-source-rcon-transport/.openspec.yaml deleted file mode 100644 index 9e5b8a1..0000000 --- a/openspec/changes/add-scum-source-rcon-transport/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-23 diff --git a/openspec/changes/add-scum-source-rcon-transport/design.md b/openspec/changes/add-scum-source-rcon-transport/design.md deleted file mode 100644 index 4199a6c..0000000 --- a/openspec/changes/add-scum-source-rcon-transport/design.md +++ /dev/null @@ -1,77 +0,0 @@ -## Context - -The prior UE4SS DLL change provisions `ue4ss/Mods/scum_simple_rcon/config.ini` before a normal SCUM start, but the declared Run `rcon` adapter is only an envelope placeholder. The shipped DLL is a Source RCON server that is intentionally loopback-only and accepts raw SCUM commands on its game-thread queue; `SendChat "message" [SteamID64]` is its supported chat form. - -Platform and Run already use leased jobs, signed Run-only input routes, scoped workspaces, and durable job state. Sending a raw admin command through the generic remote-adapter `inputs` map would persist it in the Platform job and Run journal, conflicting with the requested no-history/no-audit behavior. - -## Goals / Non-Goals - -**Goals:** - -- Let an authorized server operator send a typed chat message or raw SCUM command through the existing job channel without a second confirmation or command audit record. -- Transfer a command exactly once to the active leased Run worker without storing the raw text or RCON password in Platform persistence, job assignments, Run journals, browser DTOs, logs, or result messages. -- Bind every dispatch to a ready, selected Windows amd64 UE4SS DLL declaration and a fixed Source RCON loopback plan. -- Have Run authenticate to `127.0.0.1` using the protected generated config and implement the Source RCON wire protocol with bounded I/O. - -**Non-Goals:** - -- Do not modify, build, or redeploy the UE4SS DLL source as part of command transport. -- Do not create a generic remote TCP/RCON gateway, expose an RCON host or password, or allow browser-to-Run sockets. -- Do not persist raw command text, chat text, source responses, a command history, or per-command audit events. -- Do not retry a mutating command automatically, provide a Linux `.dll` fallback, or implement a separate Linux UE4SS `.so` runtime. - -## Decisions - -### Decision: Use a dedicated typed SCUM RCON request instead of generic remote-adapter inputs - -Platform will expose a typed server-scoped request that accepts either a chat payload (`type`, message, optional SteamID64) or a bounded raw command. It validates the source DLL grammar and formats chat as `SendChat` internally. It validates ownership, the declared RCON capability, selected runtime binding, ready extension release, Windows amd64 endpoint, and the selected profile's RCON transport before it creates a job. - -The job contains only an opaque input reference and a frozen `source-rcon` connection plan: extension key/mod key, logical config reference, and port. It contains no host, command, response, or secret. The existing generic remote-adapter endpoint remains suitable for declared non-secret inputs but is not used for raw SCUM commands. - -Alternative considered: put `command` in `ExecutionInput.Inputs`. Rejected because Platform persistence, job claims, and Run's durable journal would retain the command. - -### Decision: Use a one-time in-memory input broker fenced by the active Run lease - -Platform stores each validated command in a mutex-protected, TTL-bounded in-memory broker before the corresponding job becomes claimable. The broker key is the job ID and cannot overwrite an outstanding submission with the same idempotency key. The signed Run-only RCON-input route first verifies endpoint, session, job, lease, attempt, capability, and frozen plan, then consumes and removes the payload. - -The RCON job has exactly one attempt. A Platform restart, expired broker entry, worker crash after consume, or repeat input read fails closed rather than replaying a possibly mutating game command. Job/audit/result summaries use only generic delivery state. - -Alternative considered: encrypting a durable broker row. Rejected because the explicit product boundary is no raw command persistence; encryption would still create history and recovery/replay semantics. - -### Decision: Freeze a source-RCON loopback plan from the selected ready UE4SS extension - -Platform derives the config reference from the extension's declared DLL layout (`ue4ss/Mods//config.ini`) and freezes the published port plus mod/extension identity. Because the actual SCUM executable may be below the workspace root, Run also owns a verified deployment-state marker that records the scoped logical config location written during DLL activation. The frozen plan carries only that safe marker reference; Run validates its extension/mod/port and expected config suffix before it reads the protected file. Run accepts only the `source-rcon` kind, Windows amd64, safe logical references, and an unprivileged declared port. There is no configurable host; Run always dials `127.0.0.1:` in the server's scoped workspace. - -The generator is corrected to write the DLL's actual `bind_address=127.0.0.1` setting. Run rejects a managed config whose port or bind address no longer matches the frozen plan. - -Alternative considered: resolve paths or credentials in Platform. Rejected because Platform must not receive host paths or the generated RCON password. - -### Decision: Treat Source RCON result bodies as sensitive transient data - -Run authenticates, executes one command, and reads bounded response packets until the DLL's empty response sentinel. It classifies an `error:` response as a failed job but returns only a safe status/error code; it never includes raw command text, password, or response body in logs, artifacts, result messages, or Platform-facing payloads. - -Alternative considered: stream full RCON output to the browser. Rejected because source responses may echo command text or game/player data and would reintroduce command history through job results/logs. - -### Decision: Give the management console two direct entry points - -The existing server detail surface gets a compact chat form and a raw-command form. Both submit immediately using typed API contracts and display only the current safe queue/result status. They intentionally do not add a confirmation modal, saved form history, command list, secret field, host field, or response transcript. - -## Risks / Trade-offs - -- [Platform or worker restarts during a command] → the broker payload is gone or consumed; the job fails closed and an operator can explicitly submit a new command. -- [SCUM/UE4SS is not loaded or the port is unavailable] → Run returns a safe connection/auth/protocol failure without falling back to a remote address. -- [An RCON response echoes sensitive input] → Run only classifies it and does not surface the body. -- [A user submits an unsupported chat target or malformed text] → Platform rejects it before a job/broker entry is created. -- [A DLL declaration is still unpublished] → the feature stays unavailable until real immutable release pins are published and selected. - -## Migration Plan - -1. Deploy Platform, Run, plugin manifest, and management-console changes together. -2. Publish a real ready DLL declaration, ensure the Windows UE4SS bootstrap is present, and restart SCUM so Run writes the managed loopback config. -3. Verify `rcon.status` or a harmless `rcon.chat` through the console on a non-production server, then send a broadcast and an allowed admin command. -4. Roll back by removing the new Run capability/using the previous Platform and Run releases; no persistent commands or secrets require migration or cleanup. - -## Open Questions - -- The current SCUM manifest is intentionally unpublished; end-to-end production dispatch remains gated until the publisher supplies the real DLL URL, checksum, size, executable checksum, and UE4SS ABI. -- Future command-output streaming would need a separate transient, authorization-reviewed design rather than reusing durable job results. diff --git a/openspec/changes/add-scum-source-rcon-transport/proposal.md b/openspec/changes/add-scum-source-rcon-transport/proposal.md deleted file mode 100644 index 78f761a..0000000 --- a/openspec/changes/add-scum-source-rcon-transport/proposal.md +++ /dev/null @@ -1,30 +0,0 @@ -## Why - -The SCUM UE4SS lifecycle now provisions a protected, loopback-only Source RCON listener, but Run's declared `rcon` adapter is still a placeholder that reports success without connecting to the listener. Operators therefore cannot actually send chat text or SCUM admin commands through the platform, and raw commands must not become a durable command-history or audit feature. - -## What Changes - -- Add a typed SCUM RCON dispatch path for direct chat delivery and raw SCUM admin commands, with no secondary confirmation step and no persisted command/audit payload. -- Freeze a ready Windows UE4SS extension's safe loopback Source RCON connection metadata into a single-attempt Run job; reject unpublished, incompatible, or non-SCUM extension states before dispatch. -- Keep the raw command only in a bounded, one-time, in-memory Platform input broker. The signed active Run lease retrieves it once; database jobs, Run journals, browser responses, audit events, logs, and result messages contain no command text or RCON password. -- Replace Run's placeholder RCON adapter with a bounded Source RCON client that reads the generated protected config inside its scoped workspace, authenticates only to `127.0.0.1`, and sends the command using standard framed packets. -- Add a server-management-console RCON panel for chat broadcasts/targeted chat and raw commands. It reports safe queued/succeeded/failed state without building a command history. - -## Capabilities - -### New Capabilities - -- `scum-source-rcon-command-dispatch`: Platform authorization, one-time command delivery, safe browser contracts, and SCUM-specific dispatch constraints. -- `run-source-rcon-execution`: Run-side loopback Source RCON authentication, packet exchange, response classification, and failure handling. - -### Modified Capabilities - -- None. - -## Impact - -- `platform/`: domain/DTO/API contracts, command validation, transient input broker, fenced Run-only input endpoint, lifecycle/extension resolution, and focused tests. -- `platform_web/`: typed API client, schemas, and the existing server-detail management surface. -- `plugins/`: SCUM lifecycle capability declaration and manifest tests. -- Independent `run/`: protocol copy, Platform client, Worker dispatch, Source RCON adapter, and unit/integration tests. -- The UE4SS DLL source remains unchanged. No user-side compiler, generic remote socket, direct browser-to-RCON connection, Linux DLL substitute, raw command persistence, or command audit trail is introduced. diff --git a/openspec/changes/add-scum-source-rcon-transport/specs/run-source-rcon-execution/spec.md b/openspec/changes/add-scum-source-rcon-transport/specs/run-source-rcon-execution/spec.md deleted file mode 100644 index b53b135..0000000 --- a/openspec/changes/add-scum-source-rcon-transport/specs/run-source-rcon-execution/spec.md +++ /dev/null @@ -1,26 +0,0 @@ -## ADDED Requirements - -### Requirement: Run executes a frozen Source RCON plan over loopback -Run SHALL execute a `source-rcon` plan only for Windows amd64 and only by dialing `127.0.0.1` at the frozen declared port. It SHALL resolve the plan's generated config through the scoped workspace, read the protected password locally, and use Source RCON authentication and framed command/response packets. It SHALL not accept a browser-provided host, socket, path, or password. - -#### Scenario: Valid loopback Source RCON command completes -- **WHEN** Run receives a valid frozen plan and one-time command for a running compatible SCUM server -- **THEN** it authenticates to the local DLL listener, sends the command, consumes the terminal response sentinel, and completes the job with a safe success status. - -#### Scenario: Unsafe plan or local configuration is rejected -- **WHEN** a plan is not `source-rcon`, is non-Windows, has an unsafe config key/port, or the local config is missing, non-loopback, or inconsistent with the frozen port -- **THEN** Run fails before opening a socket or sending a command. - -### Requirement: Run bounds and redacts Source RCON I/O -Run SHALL bound command bytes, packet sizes, response bytes, response packet count, dialing, authentication, and command execution by the job context. It SHALL classify an RCON error response as a failed job but SHALL NOT persist or return the raw command, password, response body, config contents, or local path in logs, artifacts, progress, or result messages. - -#### Scenario: Source RCON error response remains private -- **WHEN** the DLL returns an `error:` response or malformed response framing -- **THEN** Run returns a safe error code/message without exposing the returned body or submitted command. - -### Requirement: Source RCON transport is at-most-once -Run SHALL retrieve the command from the active Platform input route once per job and SHALL not retry a command after input, authentication, dial, or response failure. It SHALL not fall back to the placeholder remote adapter, a generic TCP address, `rundll32`, process injection, or a Linux loader. - -#### Scenario: Lost one-time input does not replay a command -- **WHEN** Run cannot retrieve a fresh one-time command or loses execution after it was consumed -- **THEN** it fails the job safely and does not send a duplicate command. diff --git a/openspec/changes/add-scum-source-rcon-transport/specs/scum-source-rcon-command-dispatch/spec.md b/openspec/changes/add-scum-source-rcon-transport/specs/scum-source-rcon-command-dispatch/spec.md deleted file mode 100644 index 3b31df9..0000000 --- a/openspec/changes/add-scum-source-rcon-transport/specs/scum-source-rcon-command-dispatch/spec.md +++ /dev/null @@ -1,37 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform dispatches a typed SCUM Source RCON command -Platform SHALL accept a server-authorized typed request for either a SCUM chat message or a bounded raw SCUM command only when the installed plugin declares `remote.run.rcon.command`, the selected runtime profile declares the RCON transport, the endpoint reports the capability, and a selected ready Windows amd64 UE4SS DLL extension provides the Source RCON plan. Chat requests SHALL validate a type from 0 through 7, 1–1024 UTF-8 message bytes, and an optional 17-digit SteamID64 before Platform formats the DLL-supported `SendChat` command. Raw commands SHALL be non-empty UTF-8, contain no NUL/newline control framing, and fit within the declared Source RCON packet bound. - -#### Scenario: Authorized chat dispatch is queued -- **WHEN** an authorized operator submits a valid broadcast or targeted chat request for a compatible ready SCUM server -- **THEN** Platform creates one scoped `remote.run.rcon.command` job with a frozen Source RCON plan and returns only safe queued state. - -#### Scenario: Unsupported command request is rejected before dispatch -- **WHEN** a request has malformed chat data, an unsafe raw command, an undeclared RCON capability, an unpublished extension, or a non-Windows endpoint -- **THEN** Platform rejects it without creating a job or retaining command text. - -### Requirement: Raw RCON input is transient and one-time -Platform SHALL hold raw RCON command text only in a bounded in-memory one-time broker keyed by the dispatch job. The persisted job, job assignment, browser DTOs, audit events, and result messages SHALL omit command text, chat text, source responses, host paths, and RCON credentials. RCON command jobs SHALL have one attempt and SHALL not automatically retry. - -#### Scenario: Active Run lease consumes a command once -- **WHEN** the active leased Run attempt requests its RCON input -- **THEN** Platform returns the command once and removes it from the broker. - -#### Scenario: Repeated, expired, or restarted delivery fails closed -- **WHEN** a broker payload was already consumed, expired, or lost after a Platform restart -- **THEN** a later Run input request fails safely and does not replay the command. - -### Requirement: Only the active signed Run attempt receives RCON input -Platform SHALL expose RCON command input only through a signed Run-only route after validating the endpoint, session, job ID, lease token, attempt, scoped server, RCON capability, and frozen Source RCON plan. Browser and plugin APIs SHALL never receive the RCON input, password, host address, or generated local config path. - -#### Scenario: Stale or foreign lease cannot read a command -- **WHEN** a Run input request has a wrong endpoint, session, lease token, attempt, or job capability -- **THEN** Platform denies it and leaves a valid unconsumed broker payload intact. - -### Requirement: Management console provides direct chat and command controls -The server management surface SHALL render direct chat and raw-command controls only through the typed Platform API. It SHALL not request a secondary confirmation, retain a command transcript, expose RCON connection material, or show raw Source RCON replies. - -#### Scenario: Console sends a chat without creating history -- **WHEN** an authorized operator submits a valid chat form -- **THEN** the console displays the safe dispatch status and does not render the chat text as a durable command record. diff --git a/openspec/changes/add-scum-source-rcon-transport/tasks.md b/openspec/changes/add-scum-source-rcon-transport/tasks.md deleted file mode 100644 index 463acc5..0000000 --- a/openspec/changes/add-scum-source-rcon-transport/tasks.md +++ /dev/null @@ -1,22 +0,0 @@ -## 1. Platform one-time command contracts - -- [x] 1.1 Add typed Source RCON plan, chat/raw-command request, safe dispatch response, and bounded validators without exposing command or secret values in persisted job structures. -- [x] 1.2 Resolve a selected ready SCUM UE4SS extension into a Windows-only frozen plan; queue one-attempt RCON jobs through a TTL-bounded in-memory broker with no per-command audit record. -- [x] 1.3 Add the signed active-lease Run input contract, API route, DTOs, safe error behavior, and focused Platform tests for authorization, one-time consume, rejection, and redaction. - -## 2. Independent Run Source RCON execution - -- [x] 2.1 Mirror the frozen plan and one-time input protocol; add Platform client and Worker plumbing that keeps raw command text out of job journals. -- [x] 2.2 Implement scoped managed-config parsing, Windows loopback validation, bounded Source RCON authentication/packet exchange, response redaction, and safe failure codes; correct generated UE4SS config to use `bind_address`. -- [x] 2.3 Add Run tests for successful auth/command, source error, malformed/unsafe config or packets, at-most-once input, and non-Windows rejection. - -## 3. Plugin and management console - -- [x] 3.1 Declare the SCUM local lifecycle RCON capability and add manifest coverage without activating the unpublished DLL release. -- [x] 3.2 Add typed frontend client/schema contracts and a direct chat/raw-command server-detail panel with no confirmation, transcript, secret, or source-response display. -- [x] 3.3 Add focused frontend rendering/submission tests and confirm safe projections exclude RCON material. - -## 4. Verification and delivery - -- [x] 4.1 Run Platform, plugin, frontend, and Run tests; run `openspec validate add-scum-source-rcon-transport --strict` and `scripts/check-structure.sh`. -- [x] 4.2 Review scoped diffs, stage only task files in the browser and independent Run repositories, commit, and push both configured branches. diff --git a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/.openspec.yaml b/openspec/changes/add-scum-ue4ss-dll-runtime-extension/.openspec.yaml deleted file mode 100644 index 7250f8f..0000000 --- a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-22 diff --git a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/design.md b/openspec/changes/add-scum-ue4ss-dll-runtime-extension/design.md deleted file mode 100644 index 92d80f5..0000000 --- a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/design.md +++ /dev/null @@ -1,87 +0,0 @@ -## Context - -`game.scum` currently declares a Windows client-manager package that is built from a Git repository. The replacement is not a user-built executable: it is a publisher-built UE4SS `main.dll` that UE4SS loads into SCUM during the normal server process startup. The supplied RCON project requires UE4SS EngineTick and therefore cannot be loaded by an external EXE or attached safely to an already running SCUM process. - -The Platform already owns plugin manifest validation, runtime binding selection, lifecycle job dispatch, and safe browser DTOs. Run already owns bounded HTTPS dependency downloads, scoped workspaces, direct executable vectors, and lifecycle process supervision. Neither currently has a typed server DLL extension plan or a real RCON adapter. - -## Goals / Non-Goals - -**Goals:** - -- Let a plugin declare a prebuilt Windows UE4SS DLL release and update policy without exposing user-side compilation, arbitrary commands, host paths, or secrets. -- Have Run synchronize the declared release automatically before each SCUM `process.start`, only after Platform has frozen the declaration into the leased job input. -- Stage the DLL atomically below a logical server workspace, preserve the previous managed DLL for rollback, generate a local-only RCON configuration, and load through the normal UE4SS startup chain. -- Expose safe declaration and synchronization state to the existing plugin/server management views. -- Reject Linux for this Windows DLL extension explicitly. - -**Non-Goals:** - -- Do not run a compiler, CMake, UE4SS source checkout, or arbitrary executable on an operator machine. -- Do not inject a live process, use `rundll32`, `LD_PRELOAD`, remote threads, manual mapping, or a generic DLL loader. -- Do not claim a Linux `.dll` equivalent. A future Linux `.so` loader is a separate, game-specific change. -- Do not delete or migrate existing client-manager installations automatically. -- Do not add per-command confirmation or persisted raw-command history for the future RCON console. - -## Decisions - -### Decision: Model the payload as a Windows UE4SS DLL extension, not a client manager or executable - -The manifest adds `runtimeProfiles.dllExtensions`. A ready profile has a fixed release URL/checksum/size/version, Windows amd64 target, compatible SCUM executable checksum, UE4SS ABI marker, logical target key, update policy, and loopback health contract. `kind` is fixed to `ue4ss-dll`, `activation` is fixed to `server-start`, and `supportedTargets` is fixed to Windows amd64. An explicit `unpublished` declaration may document an intended release URL but cannot be selected by a lifecycle profile or frozen into a job; this prevents placeholder hashes from reaching an operator machine. - -The SCUM manifest declares the extension while retaining legacy client-manager metadata for existing installations. New server starts use the extension profile rather than building a companion executable. - -Alternative considered: reuse `clientManagers`. Rejected because a UE4SS DLL is loaded by SCUM rather than supervised as an independent component process. - -### Decision: Synchronize within the existing `process.start` lifecycle job - -Platform copies the selected DLL extension plan into the fenced start-job execution input. Run performs sync before it launches the server executable. The job is the automatic update check: every start compares the installed managed release checksum/version with the declaration and updates only when necessary. - -This avoids a new always-on download poller, preserves control/job/log/artifact channel isolation, and means Run never obtains unleased plugin configuration. A version change takes effect at the next normal server start. Future scheduled maintenance can queue an ordinary restart/start lifecycle job. - -Alternative considered: let Run periodically poll plugin URLs directly. Rejected because it makes Run independently trust mutable plugin configuration and consumes network/resources outside Platform job fencing. - -### Decision: Use release-pinned, scoped, atomic deployment - -Run accepts only HTTPS URLs with a declared `sha256:` checksum and a bounded size. It downloads to a per-server extension staging root, verifies bytes before activation, and replaces only the managed DLL path atomically. It records a small local managed-release marker and retains one previous DLL copy for rollback. It never extracts archives, follows unsafe redirects, or accepts arbitrary target paths. - -The generated `config.ini` contains a locally supplied RCON password reference/value only in the protected server workspace; it is not returned to Platform, plugins, browser DTOs, artifacts, or logs. `mods.txt` is updated idempotently using the exact declared extension key. UE4SS bootstrap files are not silently installed by this first change; the extension reports a blocked health result when the expected UE4SS layout is absent. - -Alternative considered: download a whole UE4SS/bootstrap package on every server. Rejected for the first release because shared loader ownership and rollback across other mods requires a separate extension-runtime manager. - -### Decision: Windows-only activation and explicit Linux rejection - -The supplied module links Windows APIs and produces a `.dll`. Windows activation is the standard UE4SS startup path: the already-installed UE4SS loader reads `mods.txt` and loads the DLL after SCUM starts. Run does not mount or execute the DLL itself. - -Linux would require a separately built `.so`, an independently verified UE4SS/Linux loader path, and game-specific compatibility. Run returns `unsupported_extension_platform` for this profile on Linux and does not try `LD_PRELOAD`. - -### Decision: Address compatibility is owned by the DLL release - -Native SCUM executor addresses are not configured in Platform or Run. The publisher-built DLL holds the vetted resolver table keyed by SCUM executable compatibility data and fails closed when its native probe cannot resolve exactly once. Platform/Run only select the right declared DLL release by compatible SCUM executable checksum. - -Alternative considered: Run-side signature scanning or automatic generation. Rejected because it would make the executor a reverse-engineering authority and could execute an incorrect native address. - -### Decision: Safe UI projections and no raw command history - -Plugin detail and server detail show release version, checksum prefix, loading mode, supported target, update-on-start policy, last safe sync state, and Linux incompatibility. They do not show raw paths, URLs with credentials, RCON passwords, Run sessions, or native signatures. A future RCON console is direct operator input with transient delivery; it is not a confirmation dialog or a persisted command-history feature. - -## Risks / Trade-offs - -- [A SCUM update changes native code] → DLL fails its internal probe; Run leaves the previous managed release in place and reports extension health unavailable rather than guessing an address. -- [A malicious/changed CDN response] → checksum, HTTPS validation, bounded size, and staged verification block activation. -- [UE4SS is not installed or incompatible] → sync completes the managed DLL only when safe, then reports a clear blocked health state; it does not copy proxy DLLs or alter shared loader files. -- [A server host has multiple instances] → extension target and port are scoped by the existing server workspace/runtime binding; no global path is projected. -- [Start job fails after update] → previous managed DLL is retained and a retry sees a coherent marker; full SCUM process rollback remains the existing lifecycle responsibility. -- [Linux host selected] → the job is rejected before download and UI projects the unsupported platform state. - -## Migration Plan - -1. Register a new `game.scum` manifest version with a Windows UE4SS extension profile and preserve legacy client-manager declarations. -2. On the next Windows start, Platform freezes the extension plan into the lifecycle job and Run syncs it before process launch. -3. Operators ensure the approved UE4SS runtime is already present once; the DLL and generated configuration are then managed automatically. -4. Roll back by publishing/reselecting the previous pinned DLL declaration and restarting the server; Run restores the previous managed DLL if activation fails. -5. Existing client-manager installations are not stopped, deleted, or migrated automatically. - -## Open Questions - -- A future change may manage the shared UE4SS bootstrap/runtime as a separately owned, reference-counted extension dependency. -- A future Linux SCUM/UE4SS `.so` implementation requires its own compatibility and loader design. diff --git a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/proposal.md b/openspec/changes/add-scum-ue4ss-dll-runtime-extension/proposal.md deleted file mode 100644 index f2a7862..0000000 --- a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/proposal.md +++ /dev/null @@ -1,33 +0,0 @@ -## Why - -The current SCUM plugin declares a separately built companion client, but the new SCUM RCON integration is a prebuilt UE4SS DLL that must load with the SCUM server process. Operators must not compile it, Run must not execute arbitrary binaries or inject a running process, and the platform currently has no declared, version-pinned DLL extension lifecycle. - -## What Changes - -- Add a plugin-declared Windows-only UE4SS DLL extension profile with a fixed HTTPS release URL, SHA-256 checksum, file size, compatible SCUM executable checksum, UE4SS ABI marker, logical deployment target, and loopback RCON health probe. -- Freeze the declared extension plan into the existing fenced `process.start` job. Run stages the approved DLL, generated configuration, and deterministic `mods.txt` entry before the normal SCUM process starts; there is no independent sync job or background poller. -- Add Run-side verified DLL synchronization with bounded HTTPS download, scoped file ownership, atomic replacement, rollback, no runtime process injection, and an explicit Linux rejection for the Windows DLL extension. -- Surface declared extension metadata and safe per-server state in plugin and server management views. The UI shows the release/version/checksum/loading mode and that Linux is unsupported, without exposing host paths, RCON passwords, or Run credentials. -- Replace the SCUM example's new-server dependency on the `scum-client-manager` path with the UE4SS RCON extension declaration. Existing client-manager records remain compatible and are not deleted by this change. -- Provision the extension's loopback-only Source RCON configuration for the existing declared remote-access boundary. This change does not create a second command console, per-command confirmation, or raw-command history. - -## Capabilities - -### New Capabilities - -- `plugin-runtime-dll-extensions`: Plugin manifest, Platform validation, safe projections, and server-start ordering for versioned Windows UE4SS DLL extensions. -- `run-ue4ss-dll-synchronization`: Independent Run download, staging, activation, UE4SS configuration, health preconditions, and rollback behavior for an approved UE4SS DLL extension. - -### Modified Capabilities - -- None. - -## Impact - -- `plugins/`: manifest schema, validator, SCUM example declaration, and fixtures. -- `platform/`: domain/DTO/validation/job contracts, server start dispatch, fenced extension-sync input, safe state projection, and API handlers. -- `platform_web/`: API types and existing plugin/server detail surfaces for declared extension state. -- Independent `run/`: protocol copies, extension-sync executor, generated local-only RCON configuration, tests, and documentation. It remains an independent Git repository and is not added to this repository. -- No user-side compiler, arbitrary executable launch, runtime remote-process injection, Linux DLL support, raw host-path exposure, raw RCON-password exposure, or unrelated product areas are introduced. - -The supplied CDN URL currently has no downloadable DLL. The SCUM example therefore carries an explicit unpublished declaration rather than an invented checksum, and it is not bound to `process.start` until the publisher supplies the real release pins. Ready declarations remain fully immutable and are the only declarations that can be frozen into a start job. diff --git a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/specs/plugin-runtime-dll-extensions/spec.md b/openspec/changes/add-scum-ue4ss-dll-runtime-extension/specs/plugin-runtime-dll-extensions/spec.md deleted file mode 100644 index 2ea233c..0000000 --- a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/specs/plugin-runtime-dll-extensions/spec.md +++ /dev/null @@ -1,34 +0,0 @@ -## ADDED Requirements - -### Requirement: Plugin declares a bounded Windows UE4SS DLL extension -The plugin manifest SHALL support `runtimeProfiles.dllExtensions` entries only for a fixed `ue4ss-dll` kind, Windows amd64 target, `server-start` activation, logical target key, declared DLL relative path, and update-on-start policy. A `ready` release SHALL additionally have an HTTPS release URL, SHA-256 checksum, bounded byte size, SCUM executable checksum, and UE4SS ABI marker. An `unpublished` declaration MAY document an intended release URL but SHALL not be frozen into a start job. The manifest validator SHALL reject executable references, shell arguments, Linux targets, unsafe URLs, non-DLL paths, missing ready-release integrity metadata, and unsafe logical keys. - -#### Scenario: Valid SCUM DLL declaration registers -- **WHEN** `game.scum` registers a Windows UE4SS DLL extension with all required immutable release and compatibility fields -- **THEN** Platform persists a typed declaration and exposes only a safe projection to browser clients. - -#### Scenario: Unsafe DLL declaration is rejected -- **WHEN** a manifest declares a non-HTTPS URL, missing checksum, Linux target, executable command, unsafe path, or missing compatibility checksum -- **THEN** manifest validation rejects the registration before it is persisted. - -#### Scenario: Unpublished SCUM release stays inactive -- **WHEN** a plugin documents an unpublished UE4SS DLL release before the publisher has supplied its immutable release pins -- **THEN** the declaration is safe to display but cannot be referenced by a server-start lifecycle profile or dispatched to Run. - -### Requirement: Server start freezes the declared extension plan -Platform SHALL add the selected compatible DLL extension plan to the fenced `process.start` Run job input. The frozen plan SHALL contain only logical target identifiers and release integrity/compatibility metadata, and SHALL reject a start when the selected runtime profile or endpoint platform does not support the extension. - -#### Scenario: Compatible Windows server start includes extension plan -- **WHEN** a Windows amd64 SCUM server with a selected compatible runtime profile is started -- **THEN** its Run start job includes the current immutable DLL extension plan before process execution. - -#### Scenario: Linux server is rejected without a download -- **WHEN** a Linux endpoint is selected for a UE4SS DLL extension profile -- **THEN** Platform rejects the extension path with an explicit unsupported-platform result and does not dispatch a DLL download. - -### Requirement: Browser receives a safe extension projection -Platform SHALL project extension key, display name, release version, checksum prefix, supported OS/architecture, loading mode, update-on-start policy, compatibility marker, and safe synchronization state to plugin and server management views. It SHALL NOT project RCON passwords, raw host paths, signed Run job data, native signatures, or raw local file metadata. - -#### Scenario: Server detail renders declaration without secrets -- **WHEN** an authorized operator views a SCUM plugin or server detail -- **THEN** the UI displays safe DLL extension metadata and does not contain credentials, host paths, or native resolver data. diff --git a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/specs/run-ue4ss-dll-synchronization/spec.md b/openspec/changes/add-scum-ue4ss-dll-runtime-extension/specs/run-ue4ss-dll-synchronization/spec.md deleted file mode 100644 index 4c15f2e..0000000 --- a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/specs/run-ue4ss-dll-synchronization/spec.md +++ /dev/null @@ -1,33 +0,0 @@ -## ADDED Requirements - -### Requirement: Run synchronizes a frozen DLL plan before server start -Run SHALL synchronize a declared UE4SS DLL extension before executing the associated `process.start` command. It SHALL compare the local managed-release marker against the frozen version/checksum, skip unchanged releases, and otherwise download, checksum-verify, stage, and atomically activate the declared DLL path within the scoped server workspace. - -#### Scenario: Changed approved DLL updates before launch -- **WHEN** a start job carries a valid extension plan whose release differs from the managed marker -- **THEN** Run verifies and activates the new DLL before launching the server process. - -#### Scenario: Unchanged approved DLL is reused -- **WHEN** a start job carries the same version and checksum as the managed marker -- **THEN** Run does not download the DLL again and proceeds to server launch. - -### Requirement: Run preserves DLL deployment safety boundaries -Run SHALL require HTTPS, a SHA-256 checksum, bounded content length, a safe logical target, and a declared `.dll` path. It SHALL write only managed extension files, retain one previous DLL for rollback, redact sensitive generated configuration, and never execute the DLL, inject a running process, load arbitrary libraries, or expose raw local paths/credentials through Platform-facing results. - -#### Scenario: Invalid or mismatched payload does not activate -- **WHEN** download fails, exceeds its byte limit, has a checksum mismatch, or the declared DLL path is unsafe -- **THEN** Run keeps the prior managed DLL intact and returns a safe failed start result. - -### Requirement: Run explicitly rejects Linux UE4SS DLL activation -Run SHALL reject a `ue4ss-dll` extension plan when its local platform is not Windows amd64. It SHALL not attempt to map the DLL, invoke a shell loader, or substitute `LD_PRELOAD`. - -#### Scenario: Linux extension start fails closed -- **WHEN** a Linux Run receives a UE4SS DLL extension plan -- **THEN** it returns `unsupported_extension_platform` before any download or process launch. - -### Requirement: Extension synchronization does not block independent channels -DLL download and verification SHALL run inside the claimed lifecycle job with bounded context while Run control heartbeat, job acknowledgement/result, cancellation polling, log spool upload, and artifact transfer remain independently scheduled. - -#### Scenario: Slow DLL download preserves control and log traffic -- **WHEN** a DLL download is blocked or slow during a start job -- **THEN** Run continues its control heartbeat and durable log upload within their configured deadlines. diff --git a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/tasks.md b/openspec/changes/add-scum-ue4ss-dll-runtime-extension/tasks.md deleted file mode 100644 index d80ecf0..0000000 --- a/openspec/changes/add-scum-ue4ss-dll-runtime-extension/tasks.md +++ /dev/null @@ -1,24 +0,0 @@ -## 1. Plugin and Platform contracts - -- [x] 1.1 Add the typed UE4SS DLL extension declaration to plugin schema, validator, fixtures, and the SCUM manifest with Windows-only compatibility metadata and an inactive unpublished-release guard. -- [x] 1.2 Add domain, DTO, validator, and safe API projection types for declared DLL extensions and frozen start-job extension plans. -- [x] 1.3 Freeze compatible extension plans into SCUM `process.start` jobs and reject incompatible Linux endpoint plans before Run dispatch. -- [x] 1.4 Add focused Platform tests for registration, safe projection, start-job fencing, and unsupported-platform behavior. - -## 2. Management console - -- [x] 2.1 Extend typed frontend API/schema contracts for safe DLL extension declarations. -- [x] 2.2 Render the declared Windows-only UE4SS extension and update-on-start policy in existing plugin/server detail surfaces without paths or secrets. -- [x] 2.3 Add focused frontend tests for declaration rendering and Linux unsupported state. - -## 3. Independent Run extension synchronization - -- [x] 3.1 Mirror the frozen extension plan in the independent Run protocol and validate bounded, Windows-only DLL fields. -- [x] 3.2 Implement scoped staged DLL download, checksum verification, managed-release marker, atomic activation, previous-DLL rollback, generated UE4SS mod configuration, and deterministic mods index update. -- [x] 3.3 Gate `process.start` on extension synchronization and fail closed on non-Windows targets, unsafe payloads, or missing UE4SS layout without executing/loading the DLL directly. -- [x] 3.4 Add Run unit tests for update/no-op/rollback/Linux rejection and channel independence. - -## 4. Verification and delivery - -- [x] 4.1 Run manifest, Platform, frontend, and Run focused tests; run `openspec validate add-scum-ue4ss-dll-runtime-extension --strict` and `scripts/check-structure.sh`. -- [x] 4.2 Review scoped diffs, stage only task files in browser and independent Run repositories, commit, and push both configured branches. diff --git a/openspec/changes/add-scum-versioned-gift-catalog/.openspec.yaml b/openspec/changes/add-scum-versioned-gift-catalog/.openspec.yaml deleted file mode 100644 index e8209ff..0000000 --- a/openspec/changes/add-scum-versioned-gift-catalog/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-28 diff --git a/openspec/changes/add-scum-versioned-gift-catalog/README.md b/openspec/changes/add-scum-versioned-gift-catalog/README.md deleted file mode 100644 index 6958a0c..0000000 --- a/openspec/changes/add-scum-versioned-gift-catalog/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# add-scum-versioned-gift-catalog - -SCUM versioned gift catalog and directed delivery diff --git a/openspec/changes/add-scum-versioned-gift-catalog/design.md b/openspec/changes/add-scum-versioned-gift-catalog/design.md deleted file mode 100644 index 884f3e5..0000000 --- a/openspec/changes/add-scum-versioned-gift-catalog/design.md +++ /dev/null @@ -1,47 +0,0 @@ -## Context - -The SCUM plugin already declares `reward.deliver`, and the prior game-player and player-state work owns server-local identity plus a durable approval/audit pattern. Neither establishes a platform-owned item catalog, immutable gift definition, or a reliable distinction between game delivery and player notification. This change adds those controls without extending browser access to raw game commands or connection material. - -## Goals / Non-Goals - -**Goals:** - -- Persist server-version-fenced gift catalogs, revisions, and grants in model-first storage with memory/file/MySQL implementations. -- Permit only validated item catalog references in a gift revision; freeze the revision contents and game-player identity at grant creation. -- Require a platform administrator to approve delivery, dispatch only typed `reward.deliver`, and record delivery and notification outcomes separately. -- Give the Chinese SCUM console shared-theme workflows for draft editing, preview, player selection, confirmation, approval, and readable histories. - -**Non-Goals:** - -- Commerce, shops, payment, offline inventory/database editing, raw item codes or generator syntax in browser requests, arbitrary RCON text, bulk grants, map trails, auto-retry of unknown delivery, or auto-redelivery after notification failure. - -## Decisions - -1. **Catalog ownership and version fencing.** Platform defines a small verified SCUM item catalog keyed by exact game version. A revision stores item-catalog keys, labels, quantities, and an item-catalog version; service validation rejects unknown versions, absent items, duplicate lines, and quantities beyond catalog bounds. This is safer than exposing game item IDs in page forms or accepting a client-provided command payload. - -2. **Draft then immutable revision.** A `gift_catalog` carries editable metadata and a draft revision; publishing creates an immutable `gift_revision` with a monotonic revision number. A `gift_grant` copies the selected revision ID plus a deep-frozen item snapshot and game-player record/ID/display-name snapshot. Editing or publishing later revisions therefore cannot change a queued grant. - -3. **Explicit approval and idempotency.** Any server-authorized operator creates a `pending-approval` grant with a required idempotency key. A platform administrator with the same server access revalidates target, revision, version catalog, and online eligibility before dispatch. The grant ID is the bridge idempotency key, and a duplicate create request returns the original grant rather than producing another command. - -4. **Delivery and notification are separate terminal facts.** `reward.deliver` success becomes `delivered`; command failure becomes `failed`; expiry/cancellation/missing result becomes `unknown`. Only after a confirmed delivery does Platform queue the declared targeted notification command. A notification error produces `notification_failed` while preserving the delivery result. No terminal grant, especially `unknown` or `notification_failed`, is automatically retried or redelivered. - -5. **No raw command or secret boundary crossing.** The frontend posts catalog keys, revision IDs, player record IDs, a bounded notice template, and idempotency key only. Platform composes typed bridge payloads from durable records, and browser/API projections omit run credentials, RCON strings, item codes, raw bridge payloads, and host paths. - -## Risks / Trade-offs - -- [A server version has no verified item catalog] → Editing, publication, and grant approval are rejected with an explicit unsupported-version result. -- [A selected player is offline] → Grant approval is denied before delivery; the pending record remains auditable and does not dispatch. -- [The bridge reports delivery success but notification fails] → Preserve `notification_failed` and do not attempt another item delivery. -- [The bridge result is lost] → Persist terminal `unknown`, do not infer success, and require a deliberate later operator workflow rather than automatic retry. -- [Two callers repeat a request] → Enforce a server/requester/idempotency-key uniqueness check and reuse the prior grant. - -## Migration Plan - -1. Add model-first records and repositories, then wire file/MySQL snapshots with no existing data migration required. -2. Deploy typed plugin catalog/delivery/notification schemas and Platform service/API support together; versions without a verified item catalog stay unsupported. -3. Deploy the console after APIs are available; hide all raw command details behind the API contracts. -4. Roll back by disabling the console/command declarations; immutable revision and grant history remains readable and no automatic replay is performed. - -## Open Questions - -None. The initial SCUM version and item list are deliberately small and can be extended through a future reviewed catalog revision. diff --git a/openspec/changes/add-scum-versioned-gift-catalog/proposal.md b/openspec/changes/add-scum-versioned-gift-catalog/proposal.md deleted file mode 100644 index 56979fe..0000000 --- a/openspec/changes/add-scum-versioned-gift-catalog/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -## Why - -SCUM operators can invoke a one-off reward bridge command but cannot safely curate reusable gifts, freeze what was approved, or establish whether a targeted player received an item and its notification exactly once. A version-fenced gift workflow turns that raw capability into an auditable, least-privilege server-management operation. - -## What Changes - -- Add editable SCUM gift catalogs with immutable published revisions and items sourced only from the currently verified server-version item catalog. -- Add durable, approval-gated targeted gift grants that freeze the selected revision and game-player identity before dispatching the declared `reward.deliver` command. -- Track gift delivery and directed-notification results independently, including visible notification failures and terminal unknown delivery outcomes that are never retried automatically. -- Add safe Platform APIs, SCUM plugin schemas, and Chinese console workflows for catalog editing, revision preview, player selection, confirmation, approvals, and grant history. - -## Capabilities - -### New Capabilities - -- `scum-versioned-gift-catalog`: Version-scoped SCUM gift definition, immutable revisions, approval-gated directed delivery, and safe operational history. - -### Modified Capabilities - -- None. - -## Impact - -- Affects Platform domain/model/repository/service/validation/API layers and durable metadata persistence. -- Extends the SCUM plugin's typed client-bridge declarations and schemas, plus Platform Web API contracts and SCUM player console. -- Does not introduce commerce, direct RCON/game connections, arbitrary item/generator commands, raw game credentials, or raw database writes. diff --git a/openspec/changes/add-scum-versioned-gift-catalog/specs/scum-versioned-gift-catalog/spec.md b/openspec/changes/add-scum-versioned-gift-catalog/specs/scum-versioned-gift-catalog/spec.md deleted file mode 100644 index 2d95d85..0000000 --- a/openspec/changes/add-scum-versioned-gift-catalog/specs/scum-versioned-gift-catalog/spec.md +++ /dev/null @@ -1,52 +0,0 @@ -## ADDED Requirements - -### Requirement: Version-fenced gift catalog and revisions -The system SHALL let authorized SCUM server operators create and edit gift catalog drafts whose items reference only the Platform-verified item catalog for the server's exact game version. Publishing SHALL create an immutable, monotonically versioned revision, and the system MUST reject unknown versions, stale or invalid items, duplicate items, and out-of-range quantities. - -#### Scenario: Publish validated draft -- **WHEN** an authorized operator publishes a draft containing only verified items for the current SCUM version -- **THEN** the system creates an immutable revision with its frozen item list and exposes it for preview and granting - -#### Scenario: Reject invalid item -- **WHEN** a draft references an item absent from the verified item catalog for the selected SCUM version -- **THEN** the system rejects the change without creating a publishable revision - -### Requirement: Frozen and idempotent targeted grant -The system SHALL create a durable grant from a selected published revision and local game-player record, freezing the revision contents and target identity before approval. A repeated request with the same scoped idempotency key MUST return the original grant and MUST NOT create another delivery command. - -#### Scenario: Later catalog edit does not alter a grant -- **WHEN** an operator creates a grant and subsequently edits or publishes the gift catalog -- **THEN** the existing grant retains its original revision, item snapshot, and target identity snapshot - -#### Scenario: Duplicate grant request -- **WHEN** a requester submits the same target/revision grant request again with the same idempotency key -- **THEN** the system returns the original grant and queues no duplicate delivery - -### Requirement: Approved bounded game delivery -The system SHALL require a platform administrator with target-server access to approve a pending grant and SHALL dispatch only the declared typed `reward.deliver` bridge command assembled from the frozen grant. Approval MUST reject offline targets, invalidated catalog/revision data, and unauthorized callers. - -#### Scenario: Offline player is not dispatched -- **WHEN** a platform administrator attempts to approve a grant for a player not present in the current online-player snapshot -- **THEN** approval is rejected and no delivery command is queued - -#### Scenario: Authorized approval queues delivery -- **WHEN** a platform administrator approves a valid grant for an online local game player -- **THEN** the system records the approver and queues one typed delivery command using the grant identity as its idempotency key - -### Requirement: Delivery and notification lifecycle safety -The system SHALL expose queued, delivered, notification_failed, failed, and unknown grant outcomes with readable audit evidence. A succeeded delivery followed by notification failure MUST remain visible as `notification_failed`; unknown delivery outcomes and notification failures MUST NOT automatically retry or redeliver items. - -#### Scenario: Notification failure after delivered item -- **WHEN** the delivery command succeeds and the targeted-notification command fails -- **THEN** the grant is retained as `notification_failed` with the successful delivery evidence and failed notification evidence - -#### Scenario: Unknown delivery is terminal -- **WHEN** a queued delivery command expires, is cancelled, or has no conclusive result -- **THEN** the grant becomes `unknown` and the system queues neither a retry nor another item delivery - -### Requirement: Safe console and API projections -The system SHALL provide Chinese shared-console workflows for draft/version editing, item preview, local player selection, grant confirmation, approval, and history. Browser requests and responses MUST NOT contain raw game item codes, generator commands, arbitrary RCON text, run credentials, host paths, or direct game connection data. - -#### Scenario: Safe grant submission -- **WHEN** an operator confirms a gift grant in the console -- **THEN** the browser submits only bounded catalog/revision, player-record, notice, and idempotency references and renders the returned readable lifecycle record diff --git a/openspec/changes/add-scum-versioned-gift-catalog/tasks.md b/openspec/changes/add-scum-versioned-gift-catalog/tasks.md deleted file mode 100644 index ca1dc7c..0000000 --- a/openspec/changes/add-scum-versioned-gift-catalog/tasks.md +++ /dev/null @@ -1,21 +0,0 @@ -## 1. Versioned catalog and grant lifecycle - -- [x] 1.1 Add verified SCUM item catalog, gift catalog/revision/grant domain and model types, repositories, migrations, and memory/file/MySQL persistence. -- [x] 1.2 Implement validation and service transitions for editable drafts, immutable revisions, frozen/idempotent grants, server/player ownership, online eligibility, and platform-admin approval/audit. -- [x] 1.3 Reconcile typed delivery and targeted notification results without automatic replay; retain queued, delivered, notification_failed, failed, and unknown outcomes. -- [x] 1.4 Add backend tests for frozen revisions, item/version validation, duplicate idempotency, offline targets, authorization/approval, notification failure, unknown results, and safe projections. - -## 2. API and plugin contract - -- [x] 2.1 Add named safe DTOs, authorized handlers/routes, and API tests for catalogs, revisions, grants, approval, and histories. -- [x] 2.2 Add SCUM item-catalog, reward-delivery, and targeted-notification typed bridge schemas/declarations and plugin validation tests. - -## 3. SCUM console - -- [x] 3.1 Add typed frontend API/contracts and shared-console Chinese catalog, revision preview, player selection, confirmation, approval, and history workflows. -- [x] 3.2 Add frontend tests for readable status/result rendering and absence of raw command/item/credential leakage. - -## 4. Verification - -- [x] 4.1 Run strict OpenSpec validation, focused backend, plugin, and frontend test suites, plus `scripts/check-structure.sh`. -- [x] 4.2 Stage only this task's files, commit on `main`, and push the configured remote. diff --git a/openspec/changes/allow-forced-server-deletion/.openspec.yaml b/openspec/changes/allow-forced-server-deletion/.openspec.yaml deleted file mode 100644 index 1b062d3..0000000 --- a/openspec/changes/allow-forced-server-deletion/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-04 diff --git a/openspec/changes/allow-forced-server-deletion/design.md b/openspec/changes/allow-forced-server-deletion/design.md deleted file mode 100644 index c2100d5..0000000 --- a/openspec/changes/allow-forced-server-deletion/design.md +++ /dev/null @@ -1,33 +0,0 @@ -## Context - -Server deletion is currently a password-confirmed soft delete, but active states (`running` and `installing`) are always rejected. That protects against orphaning live runtime work, but it also traps operators when the platform state is stale, the run endpoint is gone, or a lifecycle job cannot complete. - -The platform/run boundary matters here: platform can mark metadata deleted, but it must not invent game-specific stop behavior or directly manage host processes outside plugin-owned lifecycle jobs. - -## Goals / Non-Goals - -**Goals:** -- Let an owner or platform administrator force-delete a running or installing instance when they explicitly accept the operational risk. -- Keep password confirmation mandatory for normal and forced deletion. -- Preserve soft-delete history and current list filtering behavior. -- Make forced deletion visible and deliberate in the UI. - -**Non-Goals:** -- Hard-deleting server metadata or historical records. -- Killing, stopping, or cleaning up a remote process as part of delete. -- Adding run-side force-kill behavior, game-specific cleanup, or deployment target changes. -- Allowing non-owners or non-admins to delete servers. - -## Decisions - -- Extend the existing `DELETE /api/v1/server-instances/{id}` JSON body with `force` and `confirmation` fields. The existing route keeps one destructive API surface, while older clients still get the default safe rejection for running/installing instances. -- Require `force: true` plus a fixed confirmation phrase for running or installing instances. This separates accidental password-only deletion from intentional cleanup of stuck active instances. -- Keep backend enforcement in `DeleteServerInstanceForSession`. The frontend can guide the operator, but the service must remain the source of truth. -- Treat forced deletion as metadata-only. The server state becomes `deleted`; run jobs, logs, artifacts, and audit history remain visible through existing historical paths where supported. -- Show force confirmation only when the selected instance is running or installing, and keep the delete entry in the existing compact server-card action menu. - -## Risks / Trade-offs - -- [Risk] A forced delete can hide a server whose process is still alive. -> Mitigation: confirmation copy states that delete is metadata-only and does not stop the process. -- [Risk] Operators may use force instead of stopping cleanly. -> Mitigation: normal delete remains the default for stopped/ready/failed servers; active states require explicit force confirmation. -- [Risk] Existing clients may omit the new fields. -> Mitigation: the backend keeps the current rejection unless force confirmation is present. diff --git a/openspec/changes/allow-forced-server-deletion/proposal.md b/openspec/changes/allow-forced-server-deletion/proposal.md deleted file mode 100644 index ce0ea81..0000000 --- a/openspec/changes/allow-forced-server-deletion/proposal.md +++ /dev/null @@ -1,24 +0,0 @@ -## Why - -Operators need a way to remove server instances that are stuck in `running` or `installing` state when the runtime can no longer be stopped cleanly. The current delete safety rule blocks those instances forever, which leaves stale servers in the active management console. - -## What Changes - -- Add an explicit forced delete path to the existing server deletion flow. -- Preserve owner/platform-admin authorization and current-password confirmation for all server deletion. -- Require an additional force confirmation when deleting a running or installing instance. -- Keep deletion as a soft delete that marks the server instance `deleted` and preserves history. -- Make the UI expose forced deletion only through the existing destructive password confirmation dialog. - -## Capabilities - -### New Capabilities -- `server-deletion`: server instance soft deletion, including authorization, password confirmation, and explicit forced deletion of active/stuck instances. - -### Modified Capabilities - -## Impact - -- `platform/` delete DTO, API handler docs, service validation, and tests. -- `platform_web/` delete request types, client-side delete dialog, error/confirmation copy, and tests. -- No changes to run executor ownership, plugin lifecycle execution, or hard-delete persistence. diff --git a/openspec/changes/allow-forced-server-deletion/specs/server-deletion/spec.md b/openspec/changes/allow-forced-server-deletion/specs/server-deletion/spec.md deleted file mode 100644 index 2ddb2ba..0000000 --- a/openspec/changes/allow-forced-server-deletion/specs/server-deletion/spec.md +++ /dev/null @@ -1,49 +0,0 @@ -## ADDED Requirements - -### Requirement: Authorized server deletion -The system SHALL allow a server instance to be deleted only when the authenticated user is the instance owner or a platform administrator. - -#### Scenario: Owner deletes a server -- **WHEN** the instance owner submits a delete request for their server -- **THEN** the system SHALL accept the request if all other delete checks pass - -#### Scenario: Non-owner cannot delete -- **WHEN** an authenticated user who is neither the owner nor a platform administrator submits a delete request -- **THEN** the system SHALL reject the request with forbidden access - -### Requirement: Password confirmation for deletion -The system SHALL require the authenticated user to provide their current account password with every server delete request and SHALL reject the request if the password is missing or does not match the current session user. - -#### Scenario: Password mismatch -- **WHEN** the authenticated user submits the delete request with an incorrect password -- **THEN** the system SHALL reject the request with forbidden access - -#### Scenario: Password required -- **WHEN** the authenticated user submits the delete request without a password -- **THEN** the system SHALL reject the request as invalid input or forbidden access - -### Requirement: Forced active server deletion -The system SHALL reject deletion for a running or installing server instance by default, but SHALL allow the same soft deletion when the authenticated owner or platform administrator also submits an explicit forced-delete confirmation. - -#### Scenario: Running server delete without force is rejected -- **WHEN** a delete request targets a running server instance without forced-delete confirmation -- **THEN** the system SHALL reject the request and keep the server instance intact - -#### Scenario: Installing server delete without force is rejected -- **WHEN** a delete request targets an installing server instance without forced-delete confirmation -- **THEN** the system SHALL reject the request and keep the server instance intact - -#### Scenario: Running server force delete marks deleted state -- **WHEN** a valid delete request targets a running server instance with forced-delete confirmation -- **THEN** the system SHALL mark the server instance as deleted while preserving historical records - -#### Scenario: Installing server force delete marks deleted state -- **WHEN** a valid delete request targets an installing server instance with forced-delete confirmation -- **THEN** the system SHALL mark the server instance as deleted while preserving historical records - -### Requirement: Soft server removal state -The system SHALL mark deleted server instances with the deleted state and preserve historical records rather than hard-deleting metadata. - -#### Scenario: Successful deletion marks deleted state -- **WHEN** a valid delete request targets a stopped, ready, failed, or force-confirmed active server instance -- **THEN** the system SHALL mark the server instance as deleted and preserve history diff --git a/openspec/changes/allow-forced-server-deletion/tasks.md b/openspec/changes/allow-forced-server-deletion/tasks.md deleted file mode 100644 index d6b3e74..0000000 --- a/openspec/changes/allow-forced-server-deletion/tasks.md +++ /dev/null @@ -1,15 +0,0 @@ -## 1. Backend Force Delete - -- [x] 1.1 Add force-delete fields to server deletion DTO/domain request and route handling. -- [x] 1.2 Update service deletion validation so running/installing servers require explicit force confirmation. -- [x] 1.3 Add backend service/API tests for forced running and installing deletion plus default rejection. - -## 2. Frontend Confirmation - -- [x] 2.1 Extend the server deletion request type and API usage with force confirmation fields. -- [x] 2.2 Update the server list delete dialog to show active-state force warning and confirmation input. -- [x] 2.3 Update frontend tests for forced delete payloads and copy. - -## 3. Verification - -- [x] 3.1 Run OpenSpec strict validation, structure check, and focused backend/frontend tests. diff --git a/openspec/changes/architecture-delivery-stream/.openspec.yaml b/openspec/changes/architecture-delivery-stream/.openspec.yaml deleted file mode 100644 index 8e26fbe..0000000 --- a/openspec/changes/architecture-delivery-stream/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-02 diff --git a/openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md b/openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md deleted file mode 100644 index f97e93e..0000000 --- a/openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md +++ /dev/null @@ -1,55 +0,0 @@ -# Next Architecture Stream Action - -## Current Guard - -- No active guard. -- `fix-env-profile-settings` task `3.5` is complete. -- `implement-browser-acceptance-suite` is complete. -- `polish-platform-interaction-design` is complete with desktop/mobile browser walkthrough evidence, black mecha and magical-girl theme evidence, automated browser acceptance evidence, frontend verification, structure verification, and strict OpenSpec validation. - -## Current Change To Implement - -- None. The current delivery stream has no active implementation target. - -## Latest Completed Change - -- Change name: `polish-platform-interaction-design` -- Status: complete. -- Primary roots: `platform_web/` -- Completion evidence: - - `LOCAL_DEBUG_PLATFORM_PORT=18189 LOCAL_DEBUG_WEB_PORT=5183 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-acceptance scripts/browser-acceptance.sh` - - Evidence file: `/private/tmp/browser-local-debug-acceptance/browser-acceptance/browser-acceptance-evidence.json` - - `cd platform_web && npm run typecheck` - - `cd platform_web && npm test` - - `cd platform_web && npm run build` - - `scripts/check-structure.sh` - - `openspec validate polish-platform-interaction-design --strict` - -## Recommended Next Action - -No further concrete backlog item is defined in the current delivery stream. The next stream step should be one of: - -1. Archive completed OpenSpec changes, starting with `polish-platform-interaction-design`, if the user wants to finalize the completed stream state. -2. Create exactly one new OpenSpec change from fresh product/design feedback, if the user provides or approves a new concrete backlog item. - -## Prompt For The Next Chat - -```text -Continue the architecture delivery stream in /Users/tasia/Desktop/code/browser. - -Read first: -- AGENTS.md -- openspec/changes/architecture-delivery-stream/delivery-plan.md -- openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md - -Task: -- Confirm there is no active guard and no active implementation target. -- If asked to finalize completed work, archive completed OpenSpec changes according to the archive workflow. -- If asked to continue product work, create exactly one new OpenSpec change from fresh approved feedback before implementing anything. -- Run the required OpenSpec validation for any change you create or archive. -- Update delivery-plan.md and NEXT_CHANGE.md after the stream action. -``` - -## Stop Condition - -Stop after archiving completed work or preparing exactly one new approved OpenSpec change, unless the user explicitly asks to keep going. diff --git a/openspec/changes/architecture-delivery-stream/delivery-plan.md b/openspec/changes/architecture-delivery-stream/delivery-plan.md deleted file mode 100644 index 68bde33..0000000 --- a/openspec/changes/architecture-delivery-stream/delivery-plan.md +++ /dev/null @@ -1,123 +0,0 @@ -# Architecture Delivery Stream - -This file is the working progress record for the architecture stream. It is intentionally stored with the OpenSpec change so a new chat can read the current queue before creating or implementing the next change. - -## Status Legend - -- `complete`: tasks and verification evidence exist. -- `active`: current implementation target. -- `guard`: current blocker that must be closed or explicitly reprioritized before generating the next concrete OpenSpec. -- `pending`: planned but not active. -- `paused`: intentionally deferred by the user. -- `blocked`: cannot proceed without a user decision or external state change. - -## Current Guard - -No active guard. `fix-env-profile-settings` task `3.5` was completed on 2026-07-08 with an API-backed browser walkthrough for the personal settings page, and `scripts/check-structure.sh` plus `openspec validate fix-env-profile-settings --strict` passed. - -## Queue - -| Order | Status | Change | Primary Roots | Completion Gate | -| --- | --- | --- | --- | --- | -| 0 | complete | `bootstrap-game-server-platform-architecture` | all | `scripts/check-structure.sh`; `openspec validate bootstrap-game-server-platform-architecture --strict`. | -| 1 | complete | `establish-development-runtime-baseline` | all | `scripts/check-all.sh`; `scripts/check-structure.sh`; `openspec validate establish-development-runtime-baseline --strict`; browser walkthrough. | -| 2 | complete | `implement-platform-core-domain` | `platform/` | Platform domain unit tests and strict validation. | -| 3 | complete | `implement-platform-api-surface` | `platform/` | API handler tests, validation tests, strict validation. | -| 4 | complete | `implement-ai-provider-management` | `platform/`, `platform_web/` | Secret redaction tests, API tests, browser walkthrough, strict validation. | -| 5 | complete | `implement-run-control-registration` | `run/`, `platform/` | Control protocol tests, registration integration test, strict validation. | -| 6 | complete | `implement-run-job-channel` | `run/`, `platform/` | Job lifecycle tests, journal/idempotency tests, strict validation. | -| 7 | complete | `implement-log-ingest-pipeline` | `run/`, `platform/` | Spool retry tests, batch ack tests, query tests, strict validation. | -| 8 | complete | `implement-artifact-transfer-channel` | `run/`, `platform/` | Chunk/resume/checksum tests, priority isolation tests, strict validation. | -| 9 | complete | `implement-plugin-registry-and-manifest-validation` | `plugins/`, `platform/` | Schema validation tests, registry API tests, strict validation. | -| 10 | complete | `implement-plugin-bridge-and-sdk` | `plugins/`, `platform_web/`, `platform/` | Bridge permission tests, SDK type checks, strict validation. | -| 11 | complete | `implement-platform-web-console-shell` | `platform_web/` | Frontend build, route/page tests, browser walkthrough, strict validation. | -| 12 | complete | `implement-server-management-workflows` | all | Create/start/stop workflow tests, browser walkthrough, strict validation. | -| 13 | complete | `redesign-platform-web-interactions` | `platform_web/` | Frontend tests/build, browser walkthrough, strict validation. | -| 14 | complete | `fix-platform-auth-session-api` | `platform/`, `platform_web/` | Auth/session API tests, frontend auth flow tests, strict validation. | -| 15 | complete | `implement-role-scoped-server-access` | `platform/`, `platform_web/` | Role access tests, UI visibility tests, strict validation. | -| 16 | complete | `implement-platform-observability-and-config-read` | `platform/`, `run/`, `platform_web/` | Observability/config read tests, browser walkthrough, strict validation. | -| 17 | complete | `implement-plugin-marketplace-api-driven-ui` | `platform/`, `platform_web/`, `plugins/` | Marketplace API tests, frontend tests/build, browser walkthrough, strict validation. | -| 18 | complete | `implement-config-write-and-file-dispatch` | `platform/`, `run/`, `platform_web/`, `plugins/` | Config diff/write tests, file dispatch tests, browser walkthrough, strict validation. | -| 19 | complete | `implement-run-worker-real-execution` | `run/`, `platform/` | Real worker lifecycle tests, job result tests, strict validation. | -| 20 | complete | `implement-plugin-page-bridge-execution` | `platform_web/`, `plugins/`, `platform/` | Plugin page bridge tests, permission tests, strict validation. | -| 21 | complete | `implement-platform-mediated-ai-invocation` | `platform/`, `platform_web/`, `plugins/` | AI invocation tests, key redaction tests, reviewable diff tests, strict validation. | -| 22 | complete | `implement-artifact-download-and-browser-transfer` | `platform/`, `run/`, `platform_web/` | Artifact download/transfer tests, browser download walkthrough, strict validation. | -| 23 | complete | `sync-implemented-docs-and-comments` | all | Documentation/comment sync checks and strict validation. | -| 24 | complete | `implement-durable-platform-storage` | `platform/` | Durable repository tests and strict validation. | -| 25 | complete | `add-local-docker-deployment` | all | Local docker smoke path and strict validation. | -| 26 | complete | `add-mysql-platform-metadata-storage` | `platform/`, deployment | MySQL metadata tests and strict validation. | -| 27 | complete | `fix-env-profile-settings` | `platform/`, `platform_web/` | Browser walkthrough task `3.5`; `scripts/check-structure.sh`; `openspec validate fix-env-profile-settings --strict`. | -| 28 | complete | `verify-current-platform-e2e-baseline` | all | Browser walkthrough and API/run/plugin proof report that classifies every required first-party flow as real, partial, demo-only, or blocked. | -| 29 | complete | `implement-real-game-plugin-lifecycle-proof` | `plugins/`, `platform/`, `platform_web/`, `run/` | Plugin/SDK tests, platform lifecycle tests, run lifecycle tests, platform_web tests/build, API-backed browser walkthrough, `scripts/check-structure.sh`, and `openspec validate implement-real-game-plugin-lifecycle-proof --strict`. | -| 30 | complete | `harden-log-artifact-channel-isolation` | `run/`, `platform/` | Run/platform channel isolation tests, protocol docs, `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1`, `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1`, `scripts/check-structure.sh`, and `openspec validate harden-log-artifact-channel-isolation --strict`. | -| 31 | complete | `implement-local-debug-workspace` | all | Local debug docs/scripts, self-start smoke, browser walkthrough, frontend/plugin/platform/run checks, `scripts/check-structure.sh`, and `openspec validate implement-local-debug-workspace --strict`. | -| 32 | complete | `implement-browser-acceptance-suite` | `platform_web/`, all | Automated browser acceptance command, API-backed first-party route proof, plugin/server operation proof, frontend/plugin/platform/run checks, `scripts/check-structure.sh`, and `openspec validate implement-browser-acceptance-suite --strict`. | -| 33 | complete | `polish-platform-interaction-design` | `platform_web/` | Interaction/design polish criteria, desktop/mobile browser walkthroughs, automated browser acceptance, platform_web tests/build, `scripts/check-structure.sh`, and `openspec validate polish-platform-interaction-design --strict`. | - -## Next Pointer - -Read `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` before starting a fresh architecture-stream chat. That file contains the exact next action and prompt. - -## Generator Handoff Template - -Use this template when starting a fresh chat whose job is to create the next OpenSpec only: - -```text -Continue the architecture delivery stream in /Users/tasia/Desktop/code/browser. - -Read first: -- AGENTS.md -- openspec/changes/architecture-delivery-stream/delivery-plan.md -- openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md - -Task: -- Follow NEXT_CHANGE.md exactly. -- If the current guard is still open, close or explicitly record the blocker first. -- Create exactly one new OpenSpec change. -- Generate proposal.md, design.md, specs/**/*.md, and tasks.md for that one change. -- Run openspec validate --strict. -- Update NEXT_CHANGE.md to point at implementing the new change. -- Stop after the one new OpenSpec is ready; do not implement it in this chat unless explicitly asked. -``` - -## Implementation Handoff Template - -Use this template when starting a fresh implementation chat for a concrete change: - -```text -Implement OpenSpec change: - -Scope: -- Implement only openspec/changes//. -- Preserve root ownership boundaries in AGENTS.md. -- Do not add billing, cloud host sales, agent-provider/cloud-provider workflows, or unrelated marketplace features. -- Do not let browser or game management plugins access run directly; route plugin capabilities through platform-mediated contracts. -- Keep log ingest durable and independent from control, job result, and artifact/file transfer channels. - -Read first: -- AGENTS.md -- openspec/changes/architecture-delivery-stream/delivery-plan.md -- openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md -- openspec/changes/bootstrap-game-server-platform-architecture/proposal.md -- openspec/changes/bootstrap-game-server-platform-architecture/design.md -- openspec/changes//proposal.md -- openspec/changes//design.md -- openspec/changes//tasks.md - -Required closure: -- Complete the tasks in openspec/changes//tasks.md only after evidence exists. -- Run scripts/check-structure.sh. -- Run openspec validate --strict. -- Run all change-specific test/build/walkthrough commands listed in the task file. -- If frontend pages are touched, complete a browser walkthrough before claiming acceptance. -- Update delivery-plan.md and NEXT_CHANGE.md before closing. -- Stop after this change is closed; do not start the next backlog item in the same chat unless explicitly asked. -``` - -## Progress Rules - -1. Resolve the current guard before generating a new concrete product OpenSpec unless the user explicitly reprioritizes. -2. Create or implement only one concrete OpenSpec by default. -3. If implementation reveals that a pending item is too large, split it before writing product code. -4. Update this progress file and `NEXT_CHANGE.md` through an OpenSpec change when the queue order, active item, or completion gates materially change. -5. Keep final answers from implementation chats focused on changed files, verification evidence, and the next suggested backlog item. diff --git a/openspec/changes/architecture-delivery-stream/design.md b/openspec/changes/architecture-delivery-stream/design.md deleted file mode 100644 index e32a70d..0000000 --- a/openspec/changes/architecture-delivery-stream/design.md +++ /dev/null @@ -1,94 +0,0 @@ -## Context - -`bootstrap-game-server-platform-architecture` established the repository roots, ownership boundaries, and architecture contracts, but it intentionally did not build the full platform. The remaining work touches all four roots and needs to be delivered as a sequence of small OpenSpec changes so each implementation chat has a narrow scope, concrete verification commands, and clear handoff to the next change. - -The delivery stream is a process and governance layer. It does not replace the bootstrap specs. Each follow-up change must treat the bootstrap change and any archived specs as the baseline. - -## Goals / Non-Goals - -**Goals:** - -- Define one ordered backlog for the architecture implementation. -- Keep each OpenSpec change small enough for one focused implementation chat. -- Require a standard handoff block for every concrete change so the user can open a new chat and paste a precise implementation prompt. -- Require closing evidence before the next OpenSpec is created or implemented. -- Cover `platform/`, `run/`, `platform_web/`, and `plugins/` without mixing ownership boundaries. - -**Non-Goals:** - -- Do not add billing, cloud host sales, provider marketplace, or agent-provider workflows. -- Do not implement product code inside this stream change. -- Do not require automated creation or closing of chats; chat boundaries are user-operated. -- Do not redefine product scope already covered by the bootstrap architecture. - -## Decisions - -### Decision 1: Use a serial backlog, not parallel feature branches - -Only one concrete implementation OpenSpec should be active at a time unless the user explicitly pauses or reprioritizes the stream. This keeps validation evidence simple and prevents later changes from depending on unverified assumptions. - -Alternative considered: create all detailed OpenSpecs at once. Rejected because later specs would likely become stale after the first implementation changes discover concrete package, runtime, and data model constraints. - -### Decision 2: Start with development runtime baseline - -The first concrete implementation change is `establish-development-runtime-baseline`. It defines the executable skeleton, package managers, local commands, and test/verification entry points before any business capability is implemented. - -Alternative considered: start with platform domain APIs. Rejected because there is not yet a runnable backend or frontend baseline to attach tests and browser walkthroughs to. - -### Decision 3: Split the stream by dependency, not by team label - -Backlog items may touch multiple roots when the contract is cross-cutting, but each item must name its primary root ownership and forbid casual cross-root imports. Shared contracts must be generated, copied through explicit contract packages, or duplicated as documented API contracts until generation exists. - -Alternative considered: one backlog per root. Rejected because platform-run protocols, plugin bridge contracts, and frontend API clients require coordinated changes. - -### Decision 4: Every concrete change gets a handoff prompt - -Each concrete OpenSpec must end with a short implementation handoff containing the change name, exact target, required reads, verification commands, and stopping conditions. The prompt is the practical bridge between chats. - -Alternative considered: rely on OpenSpec files alone. Rejected because a new chat needs a compact instruction that prevents it from reopening already-settled scope. - -### Decision 5: Closing evidence gates progression - -A change is not considered closed until its tasks are checked with evidence. The minimum evidence is `scripts/check-structure.sh` plus `openspec validate --strict`; frontend page changes also require a browser walkthrough, and executable code changes require the relevant tests/builds documented by that change. - -Alternative considered: create the next change after implementation edits are made. Rejected because unverified work compounds defects into downstream specs. - -## Initial Delivery Queue - -| Order | Change | Primary Roots | Purpose | -| --- | --- | --- | --- | -| 0 | `bootstrap-game-server-platform-architecture` | all | Completed architecture baseline and repository skeleton. | -| 1 | `establish-development-runtime-baseline` | all | Add runnable project/tooling baselines and common verification commands. | -| 2 | `implement-platform-core-domain` | `platform/` | Add core domain, DTO, model, repository, service, validator, and route contracts for users, plugins, server instances, AI providers, jobs, artifacts, logs, and audit. | -| 3 | `implement-platform-api-surface` | `platform/` | Add HTTP API handlers, validation, error envelopes, and initial persistence wiring for the core resources. | -| 4 | `implement-ai-provider-management` | `platform/`, `platform_web/` | Store AI provider metadata safely, redact secrets, and expose first-party management APIs and UI. | -| 5 | `implement-run-control-registration` | `run/`, `platform/` | Add run hello, heartbeat, capability, version, and capacity registration. | -| 6 | `implement-run-job-channel` | `run/`, `platform/` | Add job claim, ack, progress, result, cancel, reconcile, and idempotent local journal behavior. | -| 7 | `implement-log-ingest-pipeline` | `run/`, `platform/` | Add local spool, compressed batch upload, sequence ack, retry, and platform log query metadata. | -| 8 | `implement-artifact-transfer-channel` | `run/`, `platform/` | Add chunked, resumable, checksummed, throttled artifact upload/download. | -| 9 | `implement-plugin-registry-and-manifest-validation` | `plugins/`, `platform/` | Validate plugin manifests, register installed game management plugins, and expose marketplace metadata. | -| 10 | `implement-plugin-bridge-and-sdk` | `plugins/`, `platform_web/`, `platform/` | Add safe plugin page bridge, SDK types, scoped platform abilities, and no raw key/run/path exposure. | -| 11 | `implement-platform-web-console-shell` | `platform_web/` | Add frontend app shell, routes, API client structure, theme tokens, and required first-party pages. | -| 12 | `implement-server-management-workflows` | all | Create server instances from plugins, dispatch lifecycle jobs to run, and show job/log/artifact state. | -| 13 | `implement-dev-game-plugin-proof` | `plugins/`, all | Add one development game management plugin proving multi-instance creation, logs, files, jobs, and AI assistance. | -| 14 | `implement-end-to-end-acceptance-suite` | all | Add cross-root acceptance checks and browser walkthrough coverage for the first complete workflow. | - -## Risks / Trade-offs - -- [Risk] The backlog may need to change after tooling decisions are implemented. Mitigation: update this stream through a new OpenSpec change if ordering or scope materially changes. -- [Risk] A change may grow too large for one chat. Mitigation: split it before implementation and keep the original change as a coordination parent only if needed. -- [Risk] Generated contracts may not exist early. Mitigation: use explicit copied contract files with documented ownership until generation is introduced by its own OpenSpec. -- [Risk] Chat handoff can omit important context. Mitigation: require each handoff to name exact files to read and exact commands to run. - -## Migration Plan - -1. Validate this stream change and use it as the current implementation queue. -2. Create `establish-development-runtime-baseline` as the first concrete OpenSpec. -3. In a new chat, implement only that change, run its verification, and check its tasks with evidence. -4. After closure, create or refine the next concrete OpenSpec from the queue. - -## Open Questions - -- Whether the initial persistence backend should be SQLite-first for local development or Postgres-first for production parity remains for the platform API changes. -- Whether log body storage starts as local compressed segments or a query engine adapter remains for the log ingest change. -- Whether run job transport starts as long polling or streaming remains for the run job channel change. diff --git a/openspec/changes/architecture-delivery-stream/proposal.md b/openspec/changes/architecture-delivery-stream/proposal.md deleted file mode 100644 index 4ab528e..0000000 --- a/openspec/changes/architecture-delivery-stream/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -## Why - -The bootstrap architecture is broad enough that implementing it as one large change would make review, verification, and rollback hard. The project needs an explicit OpenSpec delivery stream that breaks the platform, run executor, frontend console, and plugin system into ordered, single-session changes that can be implemented one at a time. - -## What Changes - -- Add a delivery workflow for creating and implementing architecture OpenSpec changes in dependency order. -- Define a per-change handoff format so each new chat can implement exactly one OpenSpec change without guessing scope. -- Define closing criteria for each implementation chat before the next OpenSpec change is started. -- Define the initial architecture backlog across `platform/`, `run/`, `platform_web/`, and `plugins/`. -- Keep the bootstrap architecture as the baseline and require every follow-up change to reference it instead of redefining product scope. - -## Capabilities - -### New Capabilities -- `architecture-delivery-workflow`: Ordered OpenSpec backlog, per-change handoff rules, implementation-chat closure criteria, and progress tracking for the full architecture delivery stream. - -### Modified Capabilities -- None. - -## Impact - -- Adds planning artifacts under `openspec/changes/architecture-delivery-stream/`. -- Affects how future OpenSpec changes are created, implemented, validated, and handed off between chats. -- Does not implement backend, run, frontend, or plugin runtime code directly. -- Requires future implementation chats to run `scripts/check-structure.sh` and `openspec validate --strict` before marking work complete. diff --git a/openspec/changes/architecture-delivery-stream/specs/architecture-delivery-workflow/spec.md b/openspec/changes/architecture-delivery-stream/specs/architecture-delivery-workflow/spec.md deleted file mode 100644 index a2e3697..0000000 --- a/openspec/changes/architecture-delivery-stream/specs/architecture-delivery-workflow/spec.md +++ /dev/null @@ -1,67 +0,0 @@ -## ADDED Requirements - -### Requirement: Ordered Architecture Backlog -The repository SHALL maintain an ordered architecture delivery backlog that maps each future OpenSpec change to its primary roots, purpose, dependencies, and verification expectations. - -#### Scenario: Backlog lists the next architecture change -- **WHEN** a contributor needs the next implementation target -- **THEN** the backlog identifies the next change name, affected roots, and why it follows the previous change - -#### Scenario: Backlog preserves bootstrap as baseline -- **WHEN** a follow-up change is planned -- **THEN** it references `bootstrap-game-server-platform-architecture` or archived baseline specs instead of redefining the product scope - -### Requirement: Single Active Implementation Change -The delivery workflow SHALL keep only one concrete implementation OpenSpec active at a time unless the user explicitly requests a pause, reprioritization, or parallel track. - -#### Scenario: Previous change is not closed -- **WHEN** the current implementation change has unchecked tasks or missing verification evidence -- **THEN** the next concrete implementation change is not started as active work - -#### Scenario: User requests a reprioritization -- **WHEN** the user explicitly changes the implementation order -- **THEN** the backlog is updated or superseded before the new active change is implemented - -### Requirement: Per-Change Handoff -Each concrete implementation OpenSpec SHALL include a handoff block suitable for a fresh chat, containing the change name, implementation objective, required context files, expected verification commands, and stopping conditions. - -#### Scenario: New chat starts implementation -- **WHEN** the user opens a fresh chat for a concrete change -- **THEN** the handoff block gives enough context to implement that change without expanding scope to unrelated backlog items - -#### Scenario: Handoff references verification -- **WHEN** the handoff is prepared -- **THEN** it includes `scripts/check-structure.sh`, `openspec validate --strict`, and any change-specific build, test, or browser walkthrough commands - -### Requirement: Closure Evidence -Implementation tasks SHALL remain unchecked until the implementing chat records verification evidence for the task or group of tasks. - -#### Scenario: Task is completed -- **WHEN** a task checkbox is marked complete -- **THEN** the change records the command, walkthrough, file reference, or artifact that proves the task is complete - -#### Scenario: Verification fails -- **WHEN** a required verification command fails -- **THEN** the implementation chat fixes the issue or records the blocker before the change is considered closed - -### Requirement: Ownership Boundaries -Each concrete change SHALL name its affected project roots and preserve root ownership boundaries from `AGENTS.md`. - -#### Scenario: Change touches multiple roots -- **WHEN** a change updates more than one of `platform/`, `run/`, `platform_web/`, and `plugins/` -- **THEN** the OpenSpec design explains the contract boundary and avoids casual cross-root imports - -#### Scenario: Shared contracts are needed -- **WHEN** two roots need the same request, response, or protocol shape -- **THEN** the change uses an explicit contract file, generated artifact, or documented copy boundary rather than importing implementation code across roots - -### Requirement: Progress Tracking -The delivery workflow SHALL maintain a progress record for the architecture stream that shows completed, active, pending, paused, and blocked changes. - -#### Scenario: Active change completes -- **WHEN** a concrete implementation change is closed -- **THEN** the progress record marks it complete with verification evidence and identifies the next pending change - -#### Scenario: Change is split -- **WHEN** a backlog item is too large for one implementation chat -- **THEN** the progress record replaces it with smaller ordered changes and records the reason for the split diff --git a/openspec/changes/architecture-delivery-stream/tasks.md b/openspec/changes/architecture-delivery-stream/tasks.md deleted file mode 100644 index ea38b2d..0000000 --- a/openspec/changes/architecture-delivery-stream/tasks.md +++ /dev/null @@ -1,27 +0,0 @@ -## 1. Stream Artifacts - -- [x] 1.1 Create the architecture delivery stream proposal. -- [x] 1.2 Create the delivery stream design with ordered backlog decisions. -- [x] 1.3 Create the delivery workflow spec with backlog, handoff, closure, ownership, and progress requirements. -- [x] 1.4 Create the delivery progress record with queue status and handoff template. - -## 2. First Concrete Change - -- [x] 2.1 Create the `establish-development-runtime-baseline` OpenSpec change. -- [x] 2.2 Add proposal, design, specs, and tasks for the runtime baseline change. -- [x] 2.3 Add a fresh-chat handoff block to the runtime baseline tasks. - -## 3. Verification - -- [x] 3.1 Run `scripts/check-structure.sh`. -- [x] 3.2 Run `openspec validate architecture-delivery-stream --strict`. -- [x] 3.3 Run `openspec validate establish-development-runtime-baseline --strict`. -- [x] 3.4 Confirm OpenSpec status shows both changes have required artifacts present. - -## Evidence - -- `scripts/check-structure.sh`: passed. -- `openspec validate architecture-delivery-stream --strict`: passed. -- `openspec validate establish-development-runtime-baseline --strict`: passed. -- `openspec status --change architecture-delivery-stream --json`: proposal, design, specs, and tasks present. -- `openspec status --change establish-development-runtime-baseline --json`: proposal, design, specs, and tasks present. diff --git a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/.openspec.yaml b/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/.openspec.yaml deleted file mode 100644 index ab39675..0000000 --- a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-30 diff --git a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/design.md b/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/design.md deleted file mode 100644 index 23d5542..0000000 --- a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/design.md +++ /dev/null @@ -1,61 +0,0 @@ -## Context - -Two independently reasonable decisions currently deadlock the build path. - -`platform/service/job_channel.go` strips `distribution.build` from any component-authenticated session, so a generated run cannot claim build work. `platform/service/distributions.go` derives `generate-run` availability from `svc.endpointSupports(endpoint, domain.JobCapabilityDistributionBuild)`, falling back to `run endpoint cannot build distributions`. An instance bound to its own generated run therefore fails the availability check permanently. - -`platform/service/distributions.go` picks the builder endpoint as `instance.DeploymentTargetID` when set, otherwise `instance.RunEndpointID`. Both resolve to machine-side endpoints, so building depends on a hand-maintained privileged worker being registered and online. - -`platform/service/distribution_build_jobs.go` decrypts the component key and returns `AuthKey` in `DistributionBuildInput`. Any endpoint claiming a build job receives that plaintext credential. - -`platform/validator/server_lifecycle.go` does not require a deployment target; it only requires `profileKey` when `runEndpointId` is supplied. The creation-time requirement is imposed by `platform_web/components/ServerDeploymentWorkflow.tsx`. - -## Goals / Non-Goals - -Goals: make build execution a platform responsibility with no dependency on machine-side endpoint state; reduce server creation to plugin type and server name; keep the generated-run build restriction as a security boundary; stop shipping plaintext auth keys to machine-side endpoints for builds. - -Non-goals: changing the channel model for control/jobs/logs/artifacts; changing how a registered run executes game lifecycle work; adding billing, cloud host sales, or provider workflows; removing deployment target selection from post-creation instance management. - -## Decisions - -### Platform-owned Docker builder - -The platform owns a builder that runs each distribution build in a container from a pinned image, with the run source snapshot mounted read-only and a per-job output directory mounted writable. Container-per-build keeps the existing plugin/job workspace isolation guarantee from `run-build-download-flow` and keeps the Go toolchain out of the platform runtime image. - -Alternative considered: building in-process with the platform's own Go toolchain. Rejected because it makes the toolchain a hard platform deployment dependency and gives build code the platform process's filesystem and credential reach. The container boundary is what makes it safe to hold the auth key on the platform side. - -Builder readiness is a platform-level probe, not a run endpoint capability. When the builder is unavailable, the unavailable reason names the builder so the operator is not sent looking at run endpoints. - -### Availability derivation - -`generate-run` and `generate-client-manager` availability becomes: plugin declares the capability, runtime bindings are complete, platform builder is ready. The `endpointSupports(..., JobCapabilityDistributionBuild)` term is removed from both actions. `bindingsComplete` and the plugin declaration checks stay as they are. - -### Build job identity - -Build jobs remain jobs with `distribution.build` capability so idempotency, artifact ownership (`ArtifactOwnerKindJob`), progress projection, and the `projectDistributionBuildResult` verification path are preserved unchanged. The change is who executes them: the platform builder claims and completes them internally instead of a machine-side endpoint claiming over the job channel. The existing artifact-scope assertions in `validateDistributionBuildResult` continue to guard the result. - -The capability-stripping guard in `job_channel.go` stays. With platform-side execution it becomes redundant for correctness but remains as defense in depth: a machine-side endpoint must never be assignable build work even if a future dispatch path regresses. - -### Secret handling - -`GetDistributionBuildInput` remains for legacy machine-side flows already in the field, but platform-executed builds resolve the component key internally and never place it in a job-channel response. The key reaches the builder container through the per-job input file rather than an API response, so it is never transmitted to a machine-side endpoint. - -### Creation form - -The deployment target selector is removed from the create branch of `ServerDeploymentWorkflow.tsx` rather than made optional. Leaving an optional selector preserves the original defect: the listed endpoints are still wrong choices at creation time. The run endpoint selector on the non-create branch is unaffected. The `saveAsDraft` special case for the create branch loses its reason to exist for target selection and is simplified accordingly. - -Backend validation already permits this, so no relaxation is needed there. `deploymentTargetId` remains accepted by the create DTO for post-creation and programmatic flows. - -## Risks / Trade-offs - -Docker becomes a platform deployment dependency for building. Mitigation: builder readiness is probed and surfaced as an explicit unavailable reason, so a platform without Docker degrades to "cannot build" with a clear cause rather than a misleading endpoint capability message. - -Existing instances carry `DeploymentTargetID` values pointing at privileged workers. Those bindings stay valid for non-build work; only build routing stops consulting them. - -## Migration Plan - -Availability derivation and platform-side execution land together, since changing availability alone would surface an action that cannot execute. The creation-form change is independent and can land in the same change without ordering constraints. - -## Open Questions - -Whether the builder image is built from this repository or pinned from a registry is left to implementation, provided the image reference is pinned rather than floating. diff --git a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/proposal.md b/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/proposal.md deleted file mode 100644 index 0d0a0a7..0000000 --- a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/proposal.md +++ /dev/null @@ -1,31 +0,0 @@ -## Why - -Creating a server instance currently forces the owner to pick a deployment target before any run executor exists. The target dropdown lists already-registered run endpoints, but the intended flow is create server → platform builds run → operator executes run on the machine → run registers back. At creation time there is nothing correct to select, so the field can only be filled with an unrelated endpoint or bypassed with the draft checkbox. - -Distribution building is also routed through machine-side run endpoints, while a generated run is intentionally stripped of `distribution.build` authority. Both restrictions are individually sound, but together they mean an instance bound to its own generated run can never build again: `generate-run` reports `run endpoint cannot build distributions`. Building only works when a separately maintained privileged worker endpoint happens to be registered and online, which makes the platform's core build path depend on hand-maintained machine state. - -The current build dispatch additionally hands the plaintext component `authKey` to whichever endpoint claims the build job, so a privileged worker accumulates credentials for every server it has ever built. Moving builds into a platform-owned Docker builder removes that credential egress path instead of widening it. - -## What Changes - -- Remove deployment target selection from the server creation form. Creation requires only game plugin type and server name; the run endpoint binding is established when the generated run registers itself. -- Keep deployment target and runtime profile selection available as optional actions on an already-created instance, never as creation prerequisites. -- Move `distribution.build` execution into a platform-owned Docker builder. The platform builds run and client-manager packages itself and no longer dispatches build jobs to machine-side run endpoints. -- Keep the generated-run build restriction intact as a security boundary; `generate-run` availability must no longer depend on any run endpoint advertising `distribution.build`. -- Stop exposing plaintext component auth keys over the job channel for builds executed by the platform builder. -- Add tests proving an instance bound only to its own generated run can still generate a new run distribution. - -## Capabilities - -### New Capabilities -- `platform-side-distribution-builds`: Covers platform-owned Docker build execution, creation-time field requirements, and run-endpoint-independent build availability. - -### Modified Capabilities -- `run-distribution-and-client-managers`: Build execution moves from machine-side run endpoints to the platform Docker builder; generated-run build restriction is preserved. -- `run-build-download-flow`: Build source snapshotting and artifact download must work without a privileged worker endpoint. - -## Impact - -- Affected roots: `platform/`, `platform_web/`, `scripts/`. -- Affected behavior: server creation validation, `generate-run` and `generate-client-manager` availability, job channel build dispatch, build input secret exposure. -- Verification requires `scripts/check-structure.sh`, platform tests, frontend tests, OpenSpec strict validation, and a local proof that run generation succeeds on an instance whose only endpoint is its own generated run. diff --git a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/specs/platform-side-distribution-builds/spec.md b/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/specs/platform-side-distribution-builds/spec.md deleted file mode 100644 index 6d53e24..0000000 --- a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/specs/platform-side-distribution-builds/spec.md +++ /dev/null @@ -1,68 +0,0 @@ -## ADDED Requirements - -### Requirement: Server creation requires only plugin type and server name -The system SHALL require only the game plugin type and the server name to create a server instance, and SHALL NOT require a deployment target, run endpoint, or runtime profile at creation time. - -#### Scenario: Creation form field set -- **WHEN** an owner opens the server creation workflow -- **THEN** the form requires plugin type and server name only, and presents no deployment target or run endpoint selector as a creation prerequisite - -#### Scenario: Creation without any registered endpoint -- **WHEN** an owner creates a server instance while no run endpoint is registered for that instance -- **THEN** creation succeeds and the instance is created without a deployment target binding - -#### Scenario: Binding established by run registration -- **WHEN** a generated run for that instance registers itself with the platform -- **THEN** the platform binds the instance to that run endpoint without the owner having pre-selected it - -#### Scenario: Target selection remains available after creation -- **WHEN** an owner opens an already-created instance -- **THEN** deployment target and runtime profile selection remain available as optional actions on that instance - -### Requirement: Distribution builds execute in a platform-owned Docker builder -The platform SHALL execute `distribution.build` work in a platform-owned Docker builder and SHALL NOT dispatch distribution build jobs to machine-side run endpoints. - -#### Scenario: Run distribution build execution -- **WHEN** an owner requests run generation for a server instance -- **THEN** the platform builds the package in its own Docker builder and records the resulting artifact against the build job - -#### Scenario: Client-manager distribution build execution -- **WHEN** an owner requests client-manager generation for a server instance -- **THEN** the platform builds the package in its own Docker builder and records the resulting artifact against the build job - -#### Scenario: Build failure reporting -- **WHEN** a platform Docker build fails -- **THEN** the distribution status becomes failed, the build job reports a failure, and the failure reason excludes host paths and secret values - -### Requirement: Build availability is independent of run endpoint capabilities -The system SHALL determine `generate-run` and `generate-client-manager` availability from plugin declarations, runtime bindings, and platform builder readiness, and SHALL NOT require any run endpoint to advertise `distribution.build`. - -#### Scenario: Instance bound only to its own generated run -- **WHEN** a server instance's only run endpoint is its own generated run, which holds no distribution-build authority -- **THEN** `generate-run` remains available and a new run distribution can be generated - -#### Scenario: No privileged worker endpoint registered -- **WHEN** no run endpoint advertising `distribution.build` is registered or online -- **THEN** run generation still succeeds through the platform Docker builder - -#### Scenario: Builder unavailable -- **WHEN** the platform Docker builder is unavailable -- **THEN** the unavailable reason names the platform builder rather than a run endpoint capability - -### Requirement: Generated runs hold no distribution-build authority -The system SHALL continue to deny distribution-build work to component-authenticated generated runs. This restriction is a security boundary and SHALL NOT be relaxed to unblock building. - -#### Scenario: Generated run claims a build -- **WHEN** a component-authenticated generated run claims work advertising `distribution.build` -- **THEN** the platform does not assign distribution build work to that run - -### Requirement: Platform builds do not expose plaintext component auth keys over the job channel -The system SHALL keep component auth keys inside the platform when builds are executed by the platform Docker builder, and SHALL NOT return plaintext auth keys to machine-side run endpoints for distribution builds. - -#### Scenario: Build input secret handling -- **WHEN** the platform builder assembles a package requiring a component auth key -- **THEN** the key is resolved inside the platform and is not transmitted to any machine-side run endpoint - -#### Scenario: Generated package still authenticates -- **WHEN** a package built by the platform builder registers with the platform -- **THEN** its embedded credential and key generation are accepted as before diff --git a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/tasks.md b/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/tasks.md deleted file mode 100644 index e5b673a..0000000 --- a/openspec/changes/archive/2026-07-30-platform-side-docker-distribution-builds/tasks.md +++ /dev/null @@ -1,40 +0,0 @@ -## 1. Server creation form - -- [x] 1.1 Remove the deployment target selector from the create branch of `platform_web/components/ServerDeploymentWorkflow.tsx`, keeping the run endpoint selector on the non-create branch unchanged. -- [x] 1.2 Simplify the create-branch step gating so it no longer depends on `deploymentTargetId` or on `saveAsDraft` for target selection. -- [x] 1.3 Keep `deploymentTargetId` accepted in `platform_web/schemas/serverManagement.ts` and the create DTO for post-creation and programmatic flows. -- [x] 1.4 Update or add frontend tests proving creation submits with plugin type and server name only. - -## 2. Platform Docker builder - -- [x] 2.1 Add a platform-owned builder that executes a distribution build in a container from a pinned image, with run source mounted read-only and a per-job output directory mounted writable. -- [x] 2.2 Add a builder readiness probe and expose its unavailable reason as a platform-builder reason, not a run endpoint capability reason. -- [x] 2.3 Route `distribution.build` job execution to the platform builder so the job is claimed and completed internally instead of over the job channel. -- [x] 2.4 Preserve job idempotency, `ArtifactOwnerKindJob` artifact ownership, progress projection, and the existing `validateDistributionBuildResult` artifact-scope assertions. -- [x] 2.5 Keep build workspaces isolated per plugin and per job as required by `run-build-download-flow`. - -## 3. Build availability derivation - -- [x] 3.1 Remove the `endpointSupports(..., JobCapabilityDistributionBuild)` term from `generate-run` and `generate-client-manager` availability in `platform/service/distributions.go`. -- [x] 3.2 Derive availability from plugin declaration, complete runtime bindings, and builder readiness, keeping existing binding reasons intact. -- [x] 3.3 Stop resolving a machine-side builder endpoint for build dispatch in `GenerateRunDistribution` and the client-manager build path. - -## 4. Secret handling - -- [x] 4.1 Resolve component auth keys inside the platform for builder-executed builds and pass them to the container through the per-job input rather than a job-channel response. -- [x] 4.2 Keep the `distribution.build` capability-stripping guard in `platform/service/job_channel.go` as defense in depth. -- [x] 4.3 Add a test proving builder-executed builds do not return a plaintext auth key to a machine-side endpoint. - -## 5. Tests and verification - -- [x] 5.1 Add a platform test proving an instance whose only endpoint is its own generated run can generate a new run distribution. -- [x] 5.2 Add a platform test proving run generation succeeds with no endpoint advertising `distribution.build` registered or online. -- [x] 5.3 Keep `TestCoreServiceComponentRunCannotClaimDistributionBuild` passing. -- [x] 5.4 Add a builder-unavailable test proving the reason names the platform builder. -- [x] 5.5 Run `scripts/check-structure.sh`, platform tests, frontend tests, and `openspec validate platform-side-docker-distribution-builds --strict`. -- [x] 5.6 Prove the flow end to end in local debug: create a server with plugin type and name only, generate a run, download and execute it, confirm registration and heartbeat. - -## 6. Documentation - -- [x] 6.1 Add server creation field rules and platform-side build ownership rules to `AGENTS.md`. -- [x] 6.2 Document builder configuration values operators must provide or may tune. diff --git a/openspec/changes/auto-managed-server-deployment/.openspec.yaml b/openspec/changes/auto-managed-server-deployment/.openspec.yaml deleted file mode 100644 index 08a199a..0000000 --- a/openspec/changes/auto-managed-server-deployment/.openspec.yaml +++ /dev/null @@ -1,4 +0,0 @@ -schema: spec-driven -created: 2026-07-28 -goal: Treat guided-install selection as authorization for secure automatic - deployment and recovery. diff --git a/openspec/changes/auto-managed-server-deployment/README.md b/openspec/changes/auto-managed-server-deployment/README.md deleted file mode 100644 index 35123d3..0000000 --- a/openspec/changes/auto-managed-server-deployment/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# auto-managed-server-deployment - -Automatically deploy guided server installations when their dedicated Run registers. diff --git a/openspec/changes/auto-managed-server-deployment/design.md b/openspec/changes/auto-managed-server-deployment/design.md deleted file mode 100644 index 6337d78..0000000 --- a/openspec/changes/auto-managed-server-deployment/design.md +++ /dev/null @@ -1,20 +0,0 @@ -## Decision - -The Platform owns the desired deployment definition. A successful, component-authenticated hello from the reserved dedicated Run endpoint is the trigger to enqueue the first guided installation. The enqueue uses a stable idempotency key derived from the server and deployment revision, so reconnects cannot duplicate work. - -The automatic path is restricted to target-bound `guided-install` servers in `draft`. Existing-server and custom-command modes remain explicit because a missing directory can be intentional or user-owned. A future reconcile capability may safely repair guided deployments after an attested drift check; it must not be simulated by blindly reinstalling on every hello. - -## Flow - -1. Create stores a target-bound draft and protected guided definition. -2. The user generates, downloads, and starts the dedicated Run. -3. Platform validates and persists the component hello/session. -4. Platform atomically advances the draft to installing and creates one `process.install` job. -5. Run claims the job through the existing job channel. - -## Boundaries - -- No host path, command, credential, or socket is returned to Platform Web or plugins. -- A running server is never overwritten by this registration trigger. -- Failed jobs use the existing bounded retry policy; reconnects do not create unbounded retries. -- The former deploy endpoint may remain for compatibility, but is not part of the normal user journey. diff --git a/openspec/changes/auto-managed-server-deployment/proposal.md b/openspec/changes/auto-managed-server-deployment/proposal.md deleted file mode 100644 index eb49e02..0000000 --- a/openspec/changes/auto-managed-server-deployment/proposal.md +++ /dev/null @@ -1,15 +0,0 @@ -## Why - -Creating a guided server already captures the user's installation intent, directory, and game configuration. Requiring a second Deploy click after the dedicated Run registers exposes an internal bootstrap stage and leaves a healthy Run idle. - -## What Changes - -- Treat `guided-install` as authorization to deploy automatically once the server-scoped Run successfully registers. -- Keep existing-server and custom-command definitions non-destructive: registration never silently reinstalls them. -- Remove Deploy/Retry deploy from the normal server-detail flow; registration and durable job state become the source of deployment progress. - -## Impact - -- `platform/` schedules the initial fenced install from successful dedicated Run registration. -- `platform_web/` presents registration as an automatic deployment wait state rather than an operator action. -- The independent Run contract remains channelized; its SCUM executor work is validated separately and is not exposed to the browser. diff --git a/openspec/changes/auto-managed-server-deployment/specs/managed-server-deployment/spec.md b/openspec/changes/auto-managed-server-deployment/specs/managed-server-deployment/spec.md deleted file mode 100644 index e4104d6..0000000 --- a/openspec/changes/auto-managed-server-deployment/specs/managed-server-deployment/spec.md +++ /dev/null @@ -1,28 +0,0 @@ -## ADDED Requirements - -### Requirement: Guided creation authorizes automatic initial deployment -The Platform SHALL queue one fenced `process.install` job when the dedicated Run for a target-bound draft with a `guided-install` definition successfully registers. - -#### Scenario: Dedicated Run registers for a guided draft -- **WHEN** the Run presents the valid server-scoped component identity for a guided draft -- **THEN** the Platform persists the session and queues the guided install using the stored deployment revision -- **AND** the server transitions to `installing` without another browser action - -#### Scenario: The dedicated Run reconnects -- **WHEN** the Run registers again after the automatic installation has been queued -- **THEN** the Platform does not create another installation job for the same deployment revision - -### Requirement: Automatic registration dispatch is non-destructive outside guided installs -The Platform SHALL NOT automatically reinstall existing-server or custom-command deployments solely because their Run registers. - -#### Scenario: Existing server Run registers -- **WHEN** a target-bound draft uses `existing-server` and its dedicated Run registers -- **THEN** the Platform records the Run session without creating an installation job - -### Requirement: Normal server management does not require a manual deployment click -The management console SHALL present guided deployment as automatically pending after dedicated Run registration rather than as a Deploy or Retry deploy button. - -#### Scenario: Guided draft awaits Run registration -- **WHEN** an operator opens a newly created guided draft before its dedicated Run has registered -- **THEN** the console directs the operator to generate and start the dedicated Run -- **AND** it does not offer a separate Deploy or Retry deploy action diff --git a/openspec/changes/auto-managed-server-deployment/tasks.md b/openspec/changes/auto-managed-server-deployment/tasks.md deleted file mode 100644 index d26e7cb..0000000 --- a/openspec/changes/auto-managed-server-deployment/tasks.md +++ /dev/null @@ -1,13 +0,0 @@ -## 1. Platform automatic dispatch - -- [x] 1.1 Queue a fenced guided install after an accepted dedicated Run hello, with stable revision idempotency and no duplicate reconnect dispatch. -- [x] 1.2 Cover guided automatic dispatch and non-guided no-op behavior with service tests. - -## 2. Management workflow - -- [x] 2.1 Remove the normal manual Deploy/Retry deploy controls and describe automatic deployment after Run registration. -- [x] 2.2 Update focused frontend tests for the automatic workflow. - -## 3. Verification - -- [x] 3.1 Run focused backend/frontend tests, type checking, structure validation, and strict OpenSpec validation. diff --git a/openspec/changes/batch-run-log-spool-upload/.openspec.yaml b/openspec/changes/batch-run-log-spool-upload/.openspec.yaml deleted file mode 100644 index d7bc011..0000000 --- a/openspec/changes/batch-run-log-spool-upload/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-10 diff --git a/openspec/changes/batch-run-log-spool-upload/design.md b/openspec/changes/batch-run-log-spool-upload/design.md deleted file mode 100644 index 3abbd54..0000000 --- a/openspec/changes/batch-run-log-spool-upload/design.md +++ /dev/null @@ -1,37 +0,0 @@ -## Context - -The Run process captures supervised stdout and stderr correctly, but `SpoolLogSink` persists one request per line. The durable uploader scans and uploads those files once per second within a five-second deadline. A startup burst therefore creates a durable backlog that reaches the platform long after the process emitted it. - -## Goals / Non-Goals - -**Goals:** - -- Persist and upload contiguous same-stream entries in bounded multi-entry batches. -- Drain pending log work promptly while retaining retryable, acknowledgement-driven durability. -- Keep log upload independent from control, job, and artifact work. - -**Non-Goals:** - -- Change platform log ingest routes or their contiguous-range contract. -- Add game-specific log behavior, browser polling, or unbounded in-memory buffering. - -## Decisions - -- Aggregate at the Run spool boundary. A short bounded in-memory builder groups entries only when their stream identity and sequence are contiguous, then writes one durable spool segment. This reduces both filesystem and HTTP work while preserving the existing spool retry model. Aggregating only in the uploader would retain thousands of per-line files and would not address disk pressure. -- Bound each durable batch by entry count and serialized payload size. A full batch is committed before later entries are accepted, so a crash can lose at most uncommitted in-memory lines; process capture will surface errors rather than silently discarding a committed range. A timer flush bounds latency for low-volume output. -- Flush pending batches repeatedly until no work remains or the channel work budget expires. The uploader remains in its own loop and uses a bounded context, so control and job loops remain independent. -- Keep platform acknowledgements range-based. Batches remain immutable after persistence and are removed only when the existing accepted range covers them. - -## Risks / Trade-offs - -- [Crash before a partial batch is committed] → Keep the aggregation window small and flush on process completion and uploader shutdown. -- [Large entry or encoded payload] → Commit that entry alone only when it satisfies the existing protocol bounds; reject unsafe oversized entries through the current validation path. -- [Backlog monopolizes a tick] → Use a finite per-cycle deadline and yield to the next scheduler turn. - -## Migration Plan - -Existing one-entry spool files remain valid and flush through the unchanged acknowledgement logic. New Run binaries begin producing multi-entry segments; rollback remains safe because the old spool reader already accepts a batch with multiple entries. - -## Open Questions - -- None. diff --git a/openspec/changes/batch-run-log-spool-upload/proposal.md b/openspec/changes/batch-run-log-spool-upload/proposal.md deleted file mode 100644 index e233967..0000000 --- a/openspec/changes/batch-run-log-spool-upload/proposal.md +++ /dev/null @@ -1,24 +0,0 @@ -## Why - -Run currently persists and uploads one HTTP log batch for every captured output line. High-volume startup output can create thousands of pending requests faster than the five-second uploader window can acknowledge them, leaving the management terminal far behind the supervised process. - -## What Changes - -- Coalesce contiguous pending entries from the same Run log stream into bounded durable upload batches. -- Drain available log backlog continuously within bounded upload work so current output reaches the platform promptly without blocking control, job, or artifact channels. -- Preserve per-stream sequence continuity, checksums, retry retention, and acknowledgement semantics when a batch is rebuilt or retried. - -## Capabilities - -### New Capabilities - -- `run-log-batch-upload`: Run-side durable aggregation and bounded delivery of supervised-process log streams. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `run/spool` pending-log representation and flush behavior, plus `run/runtime` process-log spooling and uploader scheduling. -- Does not change platform API routes, browser contracts, game-specific lifecycle behavior, or artifact/control/job channels. diff --git a/openspec/changes/batch-run-log-spool-upload/specs/run-log-batch-upload/spec.md b/openspec/changes/batch-run-log-spool-upload/specs/run-log-batch-upload/spec.md deleted file mode 100644 index 21e51a2..0000000 --- a/openspec/changes/batch-run-log-spool-upload/specs/run-log-batch-upload/spec.md +++ /dev/null @@ -1,30 +0,0 @@ -## ADDED Requirements - -### Requirement: Run aggregates contiguous process log entries durably -The Run log spool SHALL persist contiguous entries from one stream as bounded multi-entry batches before upload. Each persisted batch MUST retain one stream identity, an ordered contiguous sequence range, and a checksum covering its complete entries. - -#### Scenario: High-volume process output is captured -- **WHEN** a supervised process emits consecutive lines on the same declared stream -- **THEN** Run MUST persist them in bounded multi-entry batches instead of one durable upload batch per line - -#### Scenario: Stream changes or bounds are reached -- **WHEN** a line belongs to another stream or adding it would exceed an aggregation bound -- **THEN** Run MUST commit the current batch and begin a separate batch without creating a sequence gap - -### Requirement: Run drains pending log backlog within bounded channel work -The Run log uploader SHALL continue flushing acknowledged pending log batches while backlog exists, subject to its bounded log-channel work budget, without blocking control, job, or artifact channels. - -#### Scenario: Backlog is present -- **WHEN** the spool contains more than one pending log batch -- **THEN** the uploader MUST attempt consecutive batches until the backlog is drained or its work budget expires - -#### Scenario: Platform acknowledgement succeeds -- **WHEN** the platform acknowledges a multi-entry batch range -- **THEN** Run MUST remove that batch from the spool and advance the acknowledged watermark through the acknowledged sequence - -### Requirement: Retry compatibility is preserved for old and new spool segments -The Run spool SHALL retain unacknowledged aggregated batches for retry and SHALL continue to flush existing one-entry segments using the same range acknowledgement semantics. - -#### Scenario: Run restarts with pending segments -- **WHEN** Run restarts while old or aggregated log segments remain unacknowledged -- **THEN** it MUST retry each persisted segment without duplicating or skipping acknowledged sequences diff --git a/openspec/changes/batch-run-log-spool-upload/tasks.md b/openspec/changes/batch-run-log-spool-upload/tasks.md deleted file mode 100644 index ecc6c6e..0000000 --- a/openspec/changes/batch-run-log-spool-upload/tasks.md +++ /dev/null @@ -1,21 +0,0 @@ -## 1. Durable Batch Aggregation - -- [x] 1.1 Add bounded same-stream entry aggregation to the Run log spool while preserving contiguous sequence and checksum validation. -- [x] 1.2 Flush partial aggregation state at lifecycle/process completion and durable-uploader shutdown. - -## 2. Backlog Delivery - -- [x] 2.1 Update the log uploader to keep draining pending batches within its bounded log-channel work budget. -- [x] 2.2 Preserve retry, acknowledgement, and old one-entry spool segment compatibility. - -## 3. Verification - -- [x] 3.1 Add unit coverage for aggregation boundaries, restart/retry behavior, and multi-entry acknowledgement cleanup. -- [x] 3.2 Add runtime coverage proving a high-volume stream catches up through bounded aggregated uploads. -- [x] 3.3 Run `go test ./...` in `run/`, `scripts/check-structure.sh`, and `openspec validate batch-run-log-spool-upload --strict`. - -## Evidence - -- 2026-08-10: `go test ./...` from `run/` passed. -- 2026-08-10: `scripts/check-structure.sh` passed. -- 2026-08-10: `openspec validate batch-run-log-spool-upload --strict` passed. diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/.openspec.yaml b/openspec/changes/bootstrap-game-server-platform-architecture/.openspec.yaml deleted file mode 100644 index 8e26fbe..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-02 diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/design.md b/openspec/changes/bootstrap-game-server-platform-architecture/design.md deleted file mode 100644 index aa8b6a5..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/design.md +++ /dev/null @@ -1,111 +0,0 @@ -## Context - -The new project starts in `/Users/tasia/Desktop/code/browser` and intentionally splits the system into four subprojects: - -- `platform/`: backend control plane for users, game management plugins, server instances, AI providers, jobs, artifacts, logs, and audit. -- `run/`: machine-side executor that performs scoped process, file, log, artifact, and server lifecycle work. -- `platform_web/`: management console frontend for 首页、服务器管理、插件市场、用户管理、AI 提供商管理. -- `plugins/`: game management plugin workspace where each plugin defines how to create and manage one server type and can create many server instances. - -The previous implementation mixed API DTOs, models, store code, business logic, frontend types, runtime protocols, plugin execution, and generated rules across many directories. This design treats directory ownership and validation as product requirements, not style preferences. - -## Goals / Non-Goals - -**Goals:** - -- Make the platform a game server management platform, not a SCUM-only application. -- Keep plugin semantics narrow: plugins define server types and server management workflows; they do not own platform transport, AI credentials, or run connections. -- Give AI providers a clear role: model endpoint/key/model configuration used through platform-mediated plugin abilities. -- Split run-platform communication into control, job, log ingest, artifact, and optional game client bridge channels. -- Preserve log continuity when file transfer or plugin file operations are busy. -- Create mandatory directories for DTOs, models, schemas, shared utilities, validators, and frontend types. -- Add a repository structure checker that future changes must update when rules change. - -**Non-Goals:** - -- No billing, cloud resource sales, or SaaS marketplace features. -- No agent provider or cloud host provider system in this change. -- No direct browser-to-run or plugin-to-run connection. -- No raw UDP log transport for reliable historical logs. -- No implementation of the full backend, frontend, or run binaries in this proposal. - -## Decisions - -### Decision 1: Four subprojects are hard boundaries - -The root project SHALL contain `run/`, `platform/`, `platform_web/`, and `plugins/` only as first-class implementation roots. - -Alternative considered: one monorepo package tree with shared internal directories. Rejected because the old project already demonstrated that blurred roots let API structs, database models, protocol structs, and frontend types drift into business logic. - -### Decision 2: Plugins are game management plugins - -A plugin declares how to create and manage a class of game server. Installing `server.scum` or `server.minecraft` enables users to create multiple server instances from that plugin. - -Alternative considered: treating every game-side mod or feature as a platform plugin. Rejected because it fragments one game into many pseudo-platform units and makes server creation unclear. - -### Decision 3: AI provider management is a platform service - -AI providers store base URL, API key reference, model list, routing mode, timeout, and policy. Plugins call a platform AI invocation API with scoped purpose and inputs; plugins never receive raw keys. - -Alternative considered: plugin-owned AI provider configuration. Rejected because credentials would be duplicated, hard to audit, and unsafe for plugin frontends. - -### Decision 4: Run communication is channelized by workload - -The run executor SHALL use separate logical channels: - -- control: hello, heartbeat, capability, capacity, version. -- job: claim, ack, progress, result, cancel, reconcile. -- log ingest: compressed batches, sequence acknowledgement, local spool, retry. -- artifact: chunk upload/download, checksum, resumable transfer, throttling. -- game client bridge: optional game-inside command polling and snapshots when a game needs it. - -Alternative considered: one WebSocket with multiplexed message types. Rejected as the primary architecture because large files, long tasks, and high-volume logs can block each other and make backpressure hard to reason about. - -### Decision 5: Logs are a data pipeline - -Run SHALL collect process output and server log files into a local spool/WAL, upload compressed batches with monotonic sequence IDs, and delete local segments only after platform acknowledgement. Platform SHALL store log metadata separately from log bodies and support pluggable storage backends such as local compressed segments, Loki, ClickHouse, OpenSearch, or Elasticsearch. - -Alternative considered: browser-oriented WebSocket logs from run to platform. Rejected because historical query, GPT analysis, retry, and thousands of server streams require durable ingestion semantics. - -### Decision 6: File transfer is artifact-based - -Plugins and frontend actions SHALL reference `artifactId` or `fileRef`, not host paths or raw run connections. Artifact transfer SHALL be chunked, resumable, checksummed, rate limited, and lower priority than control and log flush. - -Alternative considered: synchronous file content inside job results. Accepted only for bounded small metadata or small text reads; rejected for general files because it can block logs and job status. - -### Decision 7: Definitions live in fixed directories - -Each backend subproject SHALL keep request/response DTOs, database models, domain types, protocol types, validation rules, shared helpers, and API route declarations in dedicated directories. Each frontend or plugin page SHALL keep API clients, page types, route definitions, schemas, bridge types, and shared utilities in dedicated directories. - -Alternative considered: colocating structs and helper functions beside handlers for speed. Rejected because the user explicitly wants structure definitions, common functions, database definitions, and API definitions in predictable locations. - -### Decision 8: Rules are validated by script - -The root `scripts/check-structure.sh` SHALL verify required directories and governance files. Future implementation changes MUST extend the checker when adding new architectural rules. - -Alternative considered: relying on AGENTS.md instructions only. Rejected because instructions alone do not prevent drift. - -## Risks / Trade-offs - -- [Risk] Directory rules may feel heavy before code exists -> Mitigation: start with lightweight presence checks and grow semantic checks with implementation. -- [Risk] HTTP polling jobs can add latency -> Mitigation: begin with pull/long-poll for NAT reliability, then add HTTP/2 or gRPC streaming only where measured latency needs it. -- [Risk] Log storage choice is premature -> Mitigation: define a storage adapter boundary and begin with local compressed segments plus metadata. -- [Risk] Plugin flexibility is reduced -> Mitigation: expose platform abilities through a typed bridge and job/artifact APIs instead of direct run access. -- [Risk] AI analysis may consume too much log context -> Mitigation: require log window extraction, redaction, summarization, and user confirmation before config writes. - -## Migration Plan - -1. Bootstrap the empty repository with four subproject roots, README files, AGENTS files, OpenSpec proposal artifacts, and the structure checker. -2. Implement minimal platform models and route contracts for game management plugins, server instances, AI providers, run sessions, jobs, artifacts, and log streams. -3. Implement run control, job claim/result, log spool/ingest, and artifact chunk APIs behind interfaces. -4. Implement platform_web pages in the required navigation set and consume only platform APIs. -5. Implement one dev game management plugin as the first proof that a plugin can create multiple server instances and use platform AI/file/log abilities. - -Rollback is simple during bootstrap: remove the new change artifacts or directories before implementation starts. After implementation starts, rollback must follow OpenSpec task boundaries. - -## Open Questions - -- Which backend database will be used first for platform metadata? -- Should log body MVP use local compressed files, ClickHouse, Loki, or OpenSearch first? -- Should the initial run job channel be short polling, long polling, or HTTP/2 streaming? -- What language/runtime should game management plugin action scripts use first? diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/proposal.md b/openspec/changes/bootstrap-game-server-platform-architecture/proposal.md deleted file mode 100644 index 3d8f7a1..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/proposal.md +++ /dev/null @@ -1,34 +0,0 @@ -## Why - -The previous SCUM-specific platform grew into a tightly coupled mix of frontend shell, backend services, run executors, client bridges, plugin runtime, file operations, log streaming, database access, and generated rules. The new project needs a clean game server management platform foundation where each subproject has explicit ownership, fixed definition directories, and verifiable change rules from the first commit. - -## What Changes - -- Create a new four-part project layout: `run/`, `platform/`, `platform_web/`, and `plugins/`. -- Define the platform as a game server management system with 首页、服务器管理、插件市场、用户管理、AI 提供商管理. -- Treat plugins as game management plugins that define how to create and manage game servers; each installed plugin can create many server instances. -- Define AI providers as GPT/OpenAI-compatible/model-provider configuration used by plugins for assisted config reading, config generation, log diagnosis, and server file suggestions. -- Separate run-platform-plugin communication into dedicated channels for control, jobs, logs, artifacts/files, and optional game client bridge behavior. -- Require logs to be a first-class ingestion pipeline with batching, compression, sequence acknowledgement, local spool, storage adapters, and browser tail as a derived view rather than the primary transport. -- Require artifact/file transfer to be chunked, resumable, checksummed, throttled, and isolated from log ingestion and control heartbeats. -- Require every subproject to keep API DTOs, database models, domain structs, shared helpers, validation rules, and frontend types in dedicated directories instead of scattering definitions through business logic. -- Add repository governance files so future changes update rules and run automated structure checks before being considered complete. - -## Capabilities - -### New Capabilities -- `project-workspace-governance`: Project layout, AGENTS rules, README contracts, and automated structure validation requirements for the four subprojects. -- `game-server-platform-core`: Core platform resources for users, game management plugins, server instances, AI providers, jobs, artifacts, logs, and audit. -- `run-execution-channel`: The run-side control, job, log ingest, artifact transfer, and optional game client bridge contracts. -- `game-plugin-system`: Game management plugin packaging, local development, manifest rules, platform bridge, plugin marketplace, and multi-instance server creation. -- `platform-web-console`: The frontend console structure, plugin page bridge rules, page model, API client layout, and design constraints. - -### Modified Capabilities -- None. This is a new project with no existing specs. - -## Impact - -- Adds project-level rules and documentation under `/Users/tasia/Desktop/code/browser`. -- Establishes OpenSpec artifacts for the initial architecture before implementation begins. -- Affects all future implementation in `run`, `platform`, `platform_web`, and `plugins`. -- Introduces an initial repository structure verifier that future changes must keep updated as rules evolve. diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/specs/game-plugin-system/spec.md b/openspec/changes/bootstrap-game-server-platform-architecture/specs/game-plugin-system/spec.md deleted file mode 100644 index ec7fc5f..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/specs/game-plugin-system/spec.md +++ /dev/null @@ -1,44 +0,0 @@ -## ADDED Requirements - -### Requirement: Game management plugin manifest defines server creation -A game management plugin SHALL provide a manifest that declares plugin identity, supported server type, create form schema, server lifecycle actions, required run capabilities, optional plugin pages, and AI/file/log permissions. - -#### Scenario: Valid plugin installed -- **WHEN** a plugin manifest declares a valid server type and required capabilities -- **THEN** the platform MUST expose it in the plugin marketplace and allow creating server instances from it - -#### Scenario: Plugin manifest requests unsafe access -- **WHEN** a plugin manifest requests direct run credentials, raw host paths, or raw AI provider keys -- **THEN** the platform MUST reject or disable that contribution - -### Requirement: Plugins use platform bridge only -Plugin page and plugin actions SHALL access platform abilities through a typed bridge or platform API and MUST NOT connect directly to run, log storage, artifact storage internals, or AI provider endpoints. - -#### Scenario: Plugin reads logs -- **WHEN** a plugin needs logs for a server instance -- **THEN** it MUST query platform log APIs by server instance, stream, time range, cursor, or analysis window - -#### Scenario: Plugin invokes AI -- **WHEN** a plugin invokes AI for config or log assistance -- **THEN** it MUST send a scoped platform AI request and receive a bounded response that excludes provider secrets - -### Requirement: Local plugin development is first-class -The project SHALL support local game management plugin development where a plugin can be registered as a dev plugin, provide UI from a dev server or static directory, and exercise real platform-run job, file, log, and AI flows against a selected test server instance. - -#### Scenario: Developer runs local plugin -- **WHEN** a developer starts a local plugin in dev mode -- **THEN** platform_web MUST show the plugin as a dev game management plugin without requiring a marketplace publish - -### Requirement: Plugin definitions are organized -The `plugins/` workspace SHALL keep manifests, schemas, UI contracts, action definitions, test fixtures, and shared plugin SDK code in predictable directories. - -#### Scenario: Plugin adds action input schema -- **WHEN** a plugin adds or changes an action input -- **THEN** the schema MUST live in a dedicated schema/contract location and tests MUST cover validation behavior - -### Requirement: Plugin can create many server instances -A game management plugin installation SHALL be reusable for multiple server instances with isolated configuration, artifacts, jobs, logs, and permissions per server instance. - -#### Scenario: Two servers from one plugin -- **WHEN** a user creates two server instances from the same plugin -- **THEN** each instance MUST have separate configuration state, run binding, log streams, and artifact references diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/specs/game-server-platform-core/spec.md b/openspec/changes/bootstrap-game-server-platform-architecture/specs/game-server-platform-core/spec.md deleted file mode 100644 index 523dc42..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/specs/game-server-platform-core/spec.md +++ /dev/null @@ -1,48 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform navigation scope -The platform SHALL define the primary product surface as 首页、服务器管理、插件市场、用户管理、AI 提供商管理. - -#### Scenario: Navigation is generated -- **WHEN** platform_web renders authenticated navigation -- **THEN** it MUST expose 首页、服务器管理、插件市场、用户管理、AI 提供商管理 as the primary areas - -### Requirement: Game management plugins create server instances -The platform SHALL model game management plugins as definitions for creating and managing game server types, and each installed game management plugin MUST be able to create multiple server instances. - -#### Scenario: Create server from plugin -- **WHEN** a user creates a server from an installed game management plugin -- **THEN** the platform MUST create a server instance linked to that plugin and a selected run endpoint - -#### Scenario: Multiple instances per plugin -- **WHEN** a game management plugin is installed once -- **THEN** users MUST be able to create more than one server instance from that plugin without reinstalling the plugin - -### Requirement: AI providers are platform-managed -The platform SHALL manage AI provider configuration for OpenAI-compatible, GPT, Claude, local, or relay endpoints, including base URL, key reference, model settings, timeout, and routing metadata. - -#### Scenario: Plugin requests AI assistance -- **WHEN** a plugin needs AI assistance for config reading, config generation, or log diagnosis -- **THEN** it MUST call a platform AI capability and MUST NOT receive raw provider API keys - -#### Scenario: AI suggests a config change -- **WHEN** AI generates a server configuration change -- **THEN** the platform MUST present a bounded diff or recommendation before any run-side file write job is dispatched - -### Requirement: Platform data definitions are centralized -The platform backend SHALL keep database models, DTOs, domain types, API route declarations, repository contracts, service interfaces, validators, and shared helpers in fixed directories. - -#### Scenario: New API added -- **WHEN** platform code adds a new HTTP/API endpoint -- **THEN** its request and response DTOs MUST be defined in the platform contract/DTO area and route declarations MUST be discoverable in the API area - -#### Scenario: New database table added -- **WHEN** platform code adds a new database table -- **THEN** its model MUST be defined in the database model area with field comments and tags before migrations or repositories reference it - -### Requirement: Platform does not expose run internals to plugins -The platform SHALL mediate all plugin access to files, jobs, logs, AI providers, and run endpoints. - -#### Scenario: Plugin requests file operation -- **WHEN** a plugin requests file access for a server instance -- **THEN** the platform MUST authorize the request and dispatch a scoped job or artifact operation instead of exposing host paths or run credentials diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/specs/platform-web-console/spec.md b/openspec/changes/bootstrap-game-server-platform-architecture/specs/platform-web-console/spec.md deleted file mode 100644 index 1b8a3c2..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/specs/platform-web-console/spec.md +++ /dev/null @@ -1,36 +0,0 @@ -## ADDED Requirements - -### Requirement: Console exposes required pages -The platform web console SHALL provide 首页、服务器管理、插件市场、用户管理、AI 提供商管理 as first-party pages. - -#### Scenario: Authenticated user opens console -- **WHEN** an authenticated user opens platform_web -- **THEN** the primary navigation MUST include 首页、服务器管理、插件市场、用户管理、AI 提供商管理 - -### Requirement: Server management uses routed or modal details -Server management SHALL avoid fixed left-list/right-detail master-detail layouts and MUST use routed details, modal details, or drawers for server detail flows. - -#### Scenario: User opens a server -- **WHEN** a user selects a server from the server list -- **THEN** platform_web MUST navigate to a detail route or open an overlay detail surface rather than permanently occupying a right-side detail pane - -### Requirement: Plugin page is hosted through platform context -Plugin page SHALL run inside a platform-controlled host that supplies theme tokens, server instance context, safe API access, AI invocation, log queries, artifact references, and job operations. - -#### Scenario: Plugin page loads -- **WHEN** a user opens an authorized plugin page for a server instance -- **THEN** the host MUST pass only safe context and MUST not expose platform auth storage, AI keys, run credentials, or host paths - -### Requirement: Frontend definitions are centralized -platform_web SHALL keep API clients, route definitions, page contracts, bridge contracts, shared component types, schemas, and validation helpers in dedicated directories. - -#### Scenario: New API call added -- **WHEN** a frontend change adds a platform API call -- **THEN** the call and related request/response types MUST live in the API/contract area rather than inside a view component - -### Requirement: Logs and files are separate user flows -The frontend SHALL treat log history/tail views and file/artifact operations as separate workflows so file operations do not imply log stream interruption. - -#### Scenario: User uploads a file while viewing logs -- **WHEN** a user uploads or downloads a server file from a plugin or file page -- **THEN** active log history or tail views MUST continue to query or subscribe through the platform log APIs independently diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/specs/project-workspace-governance/spec.md b/openspec/changes/bootstrap-game-server-platform-architecture/specs/project-workspace-governance/spec.md deleted file mode 100644 index 74f45fb..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/specs/project-workspace-governance/spec.md +++ /dev/null @@ -1,45 +0,0 @@ -## ADDED Requirements - -### Requirement: Four-root project layout -The repository SHALL use `run/`, `platform/`, `platform_web/`, and `plugins/` as the only first-class implementation roots for executor, backend, frontend, and game management plugin work. - -#### Scenario: Bootstrap layout exists -- **WHEN** a contributor inspects the repository root -- **THEN** the root MUST contain `run/`, `platform/`, `platform_web/`, and `plugins/` - -#### Scenario: New implementation is placed under the correct root -- **WHEN** a change adds executor, backend, frontend, or game management plugin implementation -- **THEN** the files MUST be placed under the matching implementation root - -### Requirement: Governance documentation is mandatory -The repository and each first-class implementation root SHALL contain an `AGENTS.md` and `README.md` that describe scope, directory rules, and verification expectations. - -#### Scenario: Root governance files exist -- **WHEN** a contributor starts work from the repository root -- **THEN** root `AGENTS.md` and `README.md` MUST explain cross-project rules and verification commands - -#### Scenario: Subproject governance files exist -- **WHEN** a contributor works inside `run/`, `platform/`, `platform_web/`, or `plugins/` -- **THEN** that directory MUST contain local `AGENTS.md` and `README.md` with root-specific rules - -### Requirement: Definition directories are fixed -Backend subprojects SHALL keep DTOs, domain structs, database models, API route definitions, validation rules, protocols, and shared helpers in dedicated directories. Frontend and plugin page subprojects SHALL keep API clients, route definitions, page types, schemas, bridge types, and shared utilities in dedicated directories. - -#### Scenario: Backend code adds a request DTO -- **WHEN** backend code adds a request or response structure -- **THEN** the structure MUST live in a dedicated DTO or contract directory rather than inside a handler function - -#### Scenario: Frontend code adds a shared type -- **WHEN** frontend code adds a shared API, route, bridge, or component type -- **THEN** the type MUST live in a dedicated type, contract, schema, or API directory rather than inside a page component - -### Requirement: Structure checks gate completion -The repository SHALL provide a structure validation command that verifies mandatory roots and governance files, and future changes MUST update that validator when adding new structure rules. - -#### Scenario: Required directory missing -- **WHEN** `scripts/check-structure.sh` runs and a required root or governance file is missing -- **THEN** the command MUST fail with a clear missing-path message - -#### Scenario: Rule changes with no validator update -- **WHEN** a change adds a new mandatory directory or governance rule -- **THEN** the change MUST update the structure checker before the task can be marked complete diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/specs/run-execution-channel/spec.md b/openspec/changes/bootstrap-game-server-platform-architecture/specs/run-execution-channel/spec.md deleted file mode 100644 index 696a1ae..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/specs/run-execution-channel/spec.md +++ /dev/null @@ -1,52 +0,0 @@ -## ADDED Requirements - -### Requirement: Run control channel is lightweight -The run executor SHALL use a lightweight control channel for hello, heartbeat, capability reporting, version reporting, and capacity reporting only. - -#### Scenario: Run starts -- **WHEN** run starts and reaches the platform -- **THEN** it MUST register through hello and report capabilities before accepting jobs - -#### Scenario: File transfer is active -- **WHEN** run is uploading or downloading large artifacts -- **THEN** control heartbeats MUST remain independent from artifact transfer progress - -### Requirement: Job channel supports lifecycle semantics -The run executor SHALL support job claim, ack, progress, result, cancel, and reconcile semantics for server lifecycle, config, database, backup, and plugin-triggered work. - -#### Scenario: Job accepted -- **WHEN** run accepts a job -- **THEN** it MUST return a structured ack before execution and terminal result after execution - -#### Scenario: Run restarts during job -- **WHEN** run restarts or reconnects after accepting a job -- **THEN** the platform MUST be able to request reconciliation using job identity or idempotency identity - -### Requirement: Logs use durable ingest -Run SHALL collect server logs into a local spool and upload compressed batches with stream identity, monotonic sequence range, checksum, and acknowledgement handling. - -#### Scenario: Platform unavailable -- **WHEN** platform log ingest is temporarily unavailable -- **THEN** run MUST retain unacknowledged log batches locally and retry without losing sequence continuity - -#### Scenario: User transfers files while logs are active -- **WHEN** artifact transfer is consuming bandwidth -- **THEN** log flush MUST keep priority over artifact chunks so historical logs continue to advance - -### Requirement: Artifact transfer is isolated -Run SHALL transfer files through an artifact channel with chunking, checksums, resume support, concurrency limits, and throttling separate from logs and control. - -#### Scenario: Plugin writes a config file -- **WHEN** a plugin asks the platform to write a config file -- **THEN** run MUST receive a scoped job that references an artifact or bounded inline content and MUST write through a safe temp-and-replace flow - -#### Scenario: Large file download active -- **WHEN** a large file download is active -- **THEN** job ack/result and log batch upload MUST NOT wait behind all artifact chunks - -### Requirement: Game client bridge is optional and separate -The system SHALL support an optional game client bridge channel for games that require in-game command execution or structured snapshots, but it MUST remain separate from run lifecycle and log ingestion channels. - -#### Scenario: Game needs in-game command bridge -- **WHEN** a game management plugin declares that a game needs an in-game client bridge -- **THEN** the platform MUST route game command polling and snapshot reporting through the client bridge contract rather than the run artifact or log channels diff --git a/openspec/changes/bootstrap-game-server-platform-architecture/tasks.md b/openspec/changes/bootstrap-game-server-platform-architecture/tasks.md deleted file mode 100644 index aa843bb..0000000 --- a/openspec/changes/bootstrap-game-server-platform-architecture/tasks.md +++ /dev/null @@ -1,43 +0,0 @@ -## 1. Workspace Bootstrap - -- [x] 1.1 Create the four first-class implementation roots: `run/`, `platform/`, `platform_web/`, and `plugins/`. -- [x] 1.2 Add root `README.md` explaining the game server management platform scope and subproject boundaries. -- [x] 1.3 Add root `AGENTS.md` with cross-project code organization, OpenSpec, verification, and no-scope-creep rules. -- [x] 1.4 Add local `README.md` and `AGENTS.md` files in each implementation root. -- [x] 1.5 Add `scripts/check-structure.sh` and wire it into documented verification commands. - -## 2. Platform Foundation Contracts - -- [x] 2.1 Define platform directories for API DTOs, database models, domain types, service contracts, repositories, validators, routes, config, and shared helpers. -- [x] 2.2 Draft platform contracts for users, game management plugins, server instances, AI providers, run endpoints, jobs, artifacts, log streams, and audit events. -- [x] 2.3 Define AI provider contract fields for provider kind, base URL, key reference, model list, relay mode, timeout, status, and redaction policy. -- [x] 2.4 Define game management plugin and server instance lifecycle contracts proving one installed plugin can create many server instances. - -## 3. Run Channel Contracts - -- [x] 3.1 Define run control contract for hello, heartbeat, capabilities, version, and capacity. -- [x] 3.2 Define run job contract for claim, ack, progress, result, cancel, reconcile, idempotency, and local journal behavior. -- [x] 3.3 Define log ingest contract for local spool, batch compression, sequence acknowledgement, retry, and storage adapter boundaries. -- [x] 3.4 Define artifact contract for chunk upload/download, checksum, resume, throttling, and priority separation from logs. -- [x] 3.5 Define optional game client bridge contract for in-game commands and structured snapshots. - -## 4. Plugin System Contracts - -- [x] 4.1 Define game management plugin manifest schema with server type, create form, lifecycle actions, run capabilities, pages, and AI/file/log permissions. -- [x] 4.2 Define local plugin development flow for dev registration, local UI hosting, action schema validation, and real platform-run flows. -- [x] 4.3 Define plugin bridge contract that blocks direct run credentials, host paths, AI keys, and storage internals. -- [x] 4.4 Add example plugin skeleton under `plugins/examples/` once implementation begins. - -## 5. Frontend Console Contracts - -- [x] 5.1 Define platform_web directories for API clients, route definitions, page contracts, schemas, bridge types, components, stores, and utilities. -- [x] 5.2 Define first-party page skeletons for 首页、服务器管理、插件市场、用户管理、AI 提供商管理. -- [x] 5.3 Define plugin page bridge UI contract for theme tokens, server instance context, safe API calls, job dispatch, log queries, artifacts, and AI invocation. -- [x] 5.4 Add frontend verification rules that prevent API types and route definitions from living inside page components. - -## 6. Verification - -- [x] 6.1 Run `scripts/check-structure.sh` and fix missing required files or directories. -- [x] 6.2 Run `openspec validate bootstrap-game-server-platform-architecture --strict` and fix proposal/spec/task issues. -- [x] 6.3 Confirm OpenSpec status shows `tasks` done or ready for apply with all required artifacts present. -- [x] 6.4 Update README verification instructions if any new required checker is added during implementation. diff --git a/openspec/changes/complete-platform-web-management-workflows/.openspec.yaml b/openspec/changes/complete-platform-web-management-workflows/.openspec.yaml deleted file mode 100644 index 8803b47..0000000 --- a/openspec/changes/complete-platform-web-management-workflows/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-12 diff --git a/openspec/changes/complete-platform-web-management-workflows/design.md b/openspec/changes/complete-platform-web-management-workflows/design.md deleted file mode 100644 index 806d981..0000000 --- a/openspec/changes/complete-platform-web-management-workflows/design.md +++ /dev/null @@ -1,102 +0,0 @@ -## Context - -The platform web console already includes the required first-party routes for overview, server management, plugin marketplace, user management, and AI provider management. The current pages are uneven: some are data-backed, some fall back to local demonstration data, and several management surfaces expose create or status actions without complete edit, deletion, retirement, or operational error handling. - -This change completes the management console without changing the product scope. Browser code continues to call platform APIs only. AI provider keys remain owned by `platform/`. Run internals, host paths, raw credentials, and direct sockets remain outside `platform_web/` and plugin pages. - -## Goals / Non-Goals - -**Goals:** - -- Make visible management data come from platform APIs in normal operation, with explicit empty/error states when APIs are unavailable. -- Complete user management for editing profile/contact fields, roles, status, and removal or deactivation. -- Complete server management for metadata edits, safe delete/archive, administrator assignment, lifecycle feedback, and detail refresh. -- Complete plugin marketplace behavior for real API data, state actions, detail refresh, and no production demo fallback masking failures. -- Complete AI provider management for correct empty-list behavior, create/update/status/test/model refresh, and deletion or disable-only retirement semantics. -- Keep frontend DTOs, validators, route contracts, and shared view contracts outside page components. -- Preserve the platform_web theme and visual system while making controls dense enough for routine operations. - -**Non-Goals:** - -- No billing, cloud host sales, provider marketplace, or agent-provider/cloud-provider workflow. -- No direct plugin-to-run access and no browser access to run credentials, host paths, or raw sockets. -- No raw AI key entry or display in `platform_web`; the UI continues to work with secret references only. -- No replacement of the hash router, design system, or global theme architecture. -- No real game process orchestration beyond existing platform-mediated workflows. - -## Decisions - -### API inventory and removal semantics - -- Users: existing `GET/POST /api/v1/users` and `GET/PUT /api/v1/users/{id}` cover list, create, edit, role updates, status updates, and safe removal by deactivation. Hard delete is not exposed; the UI labels removal as `停用` and sends `status=disabled`. -- Server instances: existing list/create/get/lifecycle/admin APIs are extended with `PUT /api/v1/server-instances/{id}` for metadata edits and `DELETE /api/v1/server-instances/{id}` for safe archive. Archive marks the server `deleted`, rejects running/installing states, hides deleted servers from normal lists, and returns them only with `state=deleted`. -- Server administrators: `GET /administrators/candidates`, `POST /administrators`, and `DELETE /administrators/{userId}` remain membership APIs; they never delete user accounts. -- Plugin marketplace: `GET /plugin-marketplace/plugins`, `GET /plugin-marketplace/plugins/{id}`, and `POST /plugin-marketplace/plugins/{id}/state` are the source of list/detail/action state. The page no longer maps `pluginCatalog` into runtime fallback data. -- AI providers: `GET/POST /ai-providers`, `GET/PUT /ai-providers/{id}`, `POST /ai-providers/{id}/status`, `POST /test`, and `GET /models` cover empty lists, save, enable/disable, metadata test, model refresh, and retirement. Retirement is disable-only (`status=disabled`) and raw key material remains rejected by validators. -- Config/logs/run/artifacts/audit: management pages continue using platform-mediated DTOs. Config API failure renders an explicit unavailable state instead of local sample config. - -Missing backend support identified during implementation was limited to server instance metadata update and archive/list semantics; these were added with DTO, service, validator, repository, route, documentation, and API tests. - -Request/response shapes remain bounded to IDs, logical refs, redacted secret refs, and platform-owned metadata. They do not include raw AI keys, raw host paths, run credentials, direct socket details, or unrestricted plugin execution fields. - -### Decision 1: Treat demo data as development-only fixtures - -Production pages will not silently replace failed API calls with `pluginCatalog`, `userAccess`, `seedProviders`, or `fallbackConfig`. Development fixtures may remain for isolated tests or explicit local-auth fallback paths, but page status must clearly show API unavailable or empty data. - -Alternative considered: keep local fallback data for visual continuity. Rejected because it hides missing backend behavior and makes the console appear functional when data was not loaded. - -### Decision 2: Add typed API contracts before page work - -Any missing update, delete, retire, archive, or detail-refresh behavior will be represented first in `platform_web/api/types.ts`, `platform_web/api/client.ts`, shared contracts, and schemas. Page components will consume those typed contracts rather than defining request or response shapes inline. - -Alternative considered: implement form submit handlers directly against ad hoc endpoints. Rejected because the repository requires API clients, DTOs, contracts, and schemas to stay outside page components. - -### Decision 3: Use deactivation or archive when hard deletion is unsafe - -Delete-like UI must respect domain safety. Users can be disabled or deleted according to platform rules. AI providers can be disabled or retired if deletion would break references. Server instances can be archived or soft-deleted if a running server or existing history prevents hard deletion. The UI must name the action accurately. - -Alternative considered: add one generic delete button everywhere. Rejected because operational resources have different safety and audit requirements. - -### Decision 4: Make fallback and failure states auditable - -Pages that cannot load API data must show an error or empty state with retry and diagnostic context. Development-only fixtures must be visually isolated and must not enable state-changing actions that imply persistence. - -Alternative considered: keep current optimistic local writes after failed API calls. Rejected because it can produce false success for management actions. - -### Decision 5: Verify UI workflows in browser after implementation - -Because this change touches frontend pages and interactions, completion requires automated tests plus a browser walkthrough. The walkthrough must include the edited pages at desktop and narrow widths and must confirm that no raw secrets, host paths, run credentials, or direct socket values are visible. - -Alternative considered: rely only on unit tests. Rejected because these workflows depend on visible controls, responsive layout, and operational feedback states. - -## Risks / Trade-offs - -- [Risk] Backend APIs may not yet expose every edit/delete operation. Mitigation: implement missing platform DTO, service, repository, handler, and tests in the same change before wiring the UI. -- [Risk] Removing silent fallbacks can make local development feel less populated. Mitigation: keep explicit local debug fixtures behind development-only paths and show clear labels when they are active. -- [Risk] Delete semantics can vary by resource. Mitigation: define resource-specific action labels and confirmation copy, and prefer disable/archive where hard deletion is unsafe. -- [Risk] This change spans several pages. Mitigation: implement one resource workflow at a time with focused tests, then finish with a full browser walkthrough and structure check. - -## Migration Plan - -1. Inventory current platform API coverage for users, server instances, marketplace plugins, AI providers, config, and maintenance data. -2. Add or extend backend routes and service behavior where required for edit, delete, archive, retire, and detail refresh operations. -3. Update frontend API types, schemas, and shared contracts before page components. -4. Replace silent demo fallbacks with explicit empty/error/local-development states. -5. Implement page-level controls, confirmations, operation feedback, and tests per resource. -6. Run frontend tests/build, relevant backend tests, browser walkthrough, `scripts/check-structure.sh`, and strict OpenSpec validation. - -Rollback is contained before downstream changes depend on these workflows: remove the new routes/client methods, restore previous page interactions, and keep read-only views. After users rely on edit/archive actions, rollback should be handled by a new OpenSpec change with data compatibility notes. - -## Open Questions - -- Resolved: server instance removal is named archive in the UI and implemented as `DELETE` to a `deleted` state so history remains visible by explicit filter. -- Resolved: AI provider removal is disable-only retirement for this implementation. -- Resolved: user removal is deactivation (`disabled`) for this implementation; hard delete is not exposed. - -## Verification Evidence - -- `go test ./...` from `platform/` passed. -- `cd platform_web && npm run typecheck && npm run test && npm run build` passed. -- `scripts/browser-acceptance.sh` passed, covering users, servers, plugin marketplace, AI providers, server detail, plugin controls, desktop/mobile widths, black-mecha/magical-girl themes, and forbidden-fragment scans. Evidence: `.local-debug/browser-acceptance/browser-acceptance-evidence.json`. -- `scripts/check-structure.sh` passed. -- `openspec validate complete-platform-web-management-workflows --strict` passed. The CLI emitted PostHog telemetry flush network errors after the success line because external network access is unavailable. diff --git a/openspec/changes/complete-platform-web-management-workflows/proposal.md b/openspec/changes/complete-platform-web-management-workflows/proposal.md deleted file mode 100644 index 8c8f968..0000000 --- a/openspec/changes/complete-platform-web-management-workflows/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -The platform web console now has the required first-party pages, but several management workflows still depend on local demonstration data or stop at create/status-only interactions. Operators need data-backed editing, deletion, and failure-visible empty states so the console can be used as a real game server operations workspace instead of a partially mocked prototype. - -## What Changes - -- Replace page-visible demo fallbacks for users, AI providers, plugin marketplace data, and server configuration with explicit API-backed loading, empty, and error states. -- Complete user management with edit, role/profile update, status change, and deletion or deactivation workflows backed by platform APIs. -- Complete server management with metadata edit actions such as rename, ownership/admin changes, safe delete/archive, and clear lifecycle/history feedback without exposing run internals. -- Complete plugin marketplace operations with real empty/error states, safe install/enable/disable handling, plugin detail refresh, and no production reliance on `pluginCatalog` fallback data. -- Complete AI provider management with reliable list-empty behavior, create/update/status/test/model refresh workflows, and deletion or disable-only retirement semantics that preserve secret boundaries. -- Keep the existing platform_web magical game operations visual direction and role-scoped navigation while making pages dense, operational, and browser-verifiable. - -## Capabilities - -### New Capabilities - -- `platform-web-management-completion`: Data-backed platform_web management workflows for users, servers, plugins, AI providers, and related operational states. - -### Modified Capabilities - -- None. This change builds on existing workflow specs and adds a frontend completion contract rather than modifying archived requirement files. - -## Impact - -- Affects `platform_web/` API types/client methods, page contracts, schemas, server/user/plugin/AI provider pages, stores, tests, and browser acceptance coverage. -- May require `platform/` DTOs, validators, services, repositories, and HTTP routes where current APIs do not support edit/delete/retire operations. -- May require updates to frontend structure checks if new shared contract/schema directories or rules are introduced. -- Must not add billing, cloud host sales, agent-provider/cloud-provider workflows, unrelated SaaS marketplace features, raw AI key exposure, raw host path exposure, direct run sockets, or plugin access to platform secrets. diff --git a/openspec/changes/complete-platform-web-management-workflows/specs/platform-web-management-completion/spec.md b/openspec/changes/complete-platform-web-management-workflows/specs/platform-web-management-completion/spec.md deleted file mode 100644 index b7e4038..0000000 --- a/openspec/changes/complete-platform-web-management-workflows/specs/platform-web-management-completion/spec.md +++ /dev/null @@ -1,109 +0,0 @@ -## ADDED Requirements - -### Requirement: Management pages use API-backed data states -The platform web management pages SHALL use platform APIs as the source of visible operational data and SHALL render explicit loading, empty, error, or development-fixture states instead of silently substituting production data with local examples. - -#### Scenario: API returns an empty list -- **WHEN** a management page API returns an empty list for users, servers, plugins, AI providers, logs, audit events, or run endpoints -- **THEN** the page MUST render an empty state that reflects the empty API response and MUST NOT keep previously seeded demonstration rows visible - -#### Scenario: API request fails -- **WHEN** a management page API request fails in normal operation -- **THEN** the page MUST render an error state with retry affordance or diagnostic context and MUST NOT enable persistence-looking actions against local fallback data - -#### Scenario: Development fixture is active -- **WHEN** an explicit development fixture or local-auth fallback is active -- **THEN** the page MUST label the data as local development data and MUST disable or clearly reject state-changing actions that cannot be persisted - -### Requirement: Management list pages preserve full-width work surfaces -The platform web management pages SHALL keep list, grid, and table views as full-width work surfaces and SHALL put create, edit, and detail workflows in modal, drawer, or detail-route surfaces instead of permanent side panes or inline split forms. - -#### Scenario: Operator opens create, edit, or detail workflow -- **WHEN** an operator opens create, edit, or detail workflows on users, plugin marketplace, AI providers, servers, or similar management list pages -- **THEN** the page MUST keep the underlying list, grid, or table full-width and MUST render the workflow in a modal, drawer, or detail route without a permanent right-side form/detail pane - -### Requirement: User management supports full account maintenance -The user management page SHALL allow authorized platform administrators to create users, edit user identity/contact fields, update roles, update status, and remove or deactivate users through platform APIs. - -#### Scenario: Administrator edits user fields -- **WHEN** a platform administrator edits a user's display name, email, phone, QQ, contact note, roles, or status -- **THEN** the frontend MUST submit a named API request, render success or failure feedback, and update the list from the persisted response - -#### Scenario: Administrator removes or deactivates a user -- **WHEN** a platform administrator confirms a user removal or deactivation action -- **THEN** the platform MUST enforce the resource safety rule and the frontend MUST render the resulting removed, disabled, or rejected state without pretending a local write succeeded - -#### Scenario: Non-admin reaches user management -- **WHEN** a user without `users.manage` reaches the user management route directly -- **THEN** the page MUST avoid rendering account maintenance controls and MUST return or explain the authorized workspace state - -### Requirement: Server management supports metadata edit and safe removal -The server management workspace SHALL allow authorized users to create server instances, edit server metadata, manage server administrators, start and stop eligible instances, and archive or delete safe instances through platform-mediated APIs. - -#### Scenario: Operator edits server metadata -- **WHEN** an authorized operator updates a server name, ownership-visible metadata, or other editable server fields -- **THEN** the frontend MUST submit a typed platform API request and render the persisted server instance response - -#### Scenario: Operator archives or deletes a server -- **WHEN** an authorized operator confirms archive or delete for a server instance -- **THEN** the platform MUST reject unsafe states such as running instances unless the chosen operation is explicitly allowed, and the frontend MUST render the accepted or rejected result with diagnostic context - -#### Scenario: Server detail manages administrators -- **WHEN** a server owner adds or removes server administrators from the server detail page -- **THEN** the frontend MUST use platform administrator membership APIs and refresh candidate and assigned member state after the operation - -### Requirement: Plugin marketplace avoids production demo fallbacks -The plugin marketplace SHALL render platform marketplace data and state actions from platform APIs and SHALL NOT rely on `pluginCatalog` fallback data in production behavior. - -#### Scenario: Marketplace API is unavailable -- **WHEN** the marketplace list or detail API request fails -- **THEN** the marketplace page MUST show an error or explicitly labeled development fixture state and MUST NOT present local catalog rows as persisted marketplace data - -#### Scenario: Plugin state action is submitted -- **WHEN** an operator installs, enables, or disables a plugin -- **THEN** the frontend MUST call the platform marketplace state API, display the operation result, and update the selected plugin detail from the persisted response - -#### Scenario: Plugin detail is refreshed -- **WHEN** an operator selects or refreshes a plugin detail -- **THEN** the frontend MUST prefer the platform detail API response and MUST render validation, permission, lifecycle, page, bridge, and AI purpose metadata without exposing secrets or run internals - -### Requirement: AI provider management handles empty data and retirement safely -The AI provider management page SHALL handle empty API lists correctly and SHALL support create, update, enable/disable, test, model refresh, and deletion or retirement semantics without exposing raw AI key material. - -#### Scenario: AI provider API returns zero providers -- **WHEN** the AI provider list API succeeds with zero providers -- **THEN** the page MUST render an empty state or creation form and MUST NOT keep seed providers visible - -#### Scenario: Provider is saved -- **WHEN** an operator creates or updates an AI provider -- **THEN** the frontend MUST submit a named API request using secret references only and MUST render the redacted provider response - -#### Scenario: Provider is retired or deleted -- **WHEN** an operator confirms provider deletion or retirement -- **THEN** the platform MUST enforce reference safety and the frontend MUST remove, disable, or mark the provider according to the persisted response - -#### Scenario: Provider action fails -- **WHEN** provider save, status, test, model refresh, delete, or retire action fails -- **THEN** the frontend MUST display failure feedback and MUST NOT mutate local state as if the action succeeded - -### Requirement: Management completion preserves security boundaries -The completed management workflows SHALL NOT expose raw AI keys, raw host paths, run credentials, direct socket addresses, or unrestricted plugin execution controls to `platform_web` or plugin pages. - -#### Scenario: Page renders operational data -- **WHEN** any completed management page renders users, servers, plugins, AI providers, logs, audit events, artifacts, jobs, or run endpoints -- **THEN** the rendered data MUST omit raw AI keys, raw host paths, run credentials, and direct socket details - -#### Scenario: Plugin control action is rendered -- **WHEN** plugin controls or bridge actions are rendered for a server -- **THEN** the controls MUST be derived from platform-approved plugin metadata and MUST dispatch through platform APIs rather than direct run or host access - -### Requirement: Management completion is verified end to end -The change SHALL include automated tests, structure validation, strict OpenSpec validation, and browser walkthrough evidence for the completed management workflows. - -#### Scenario: Verification commands run -- **WHEN** the implementation is complete -- **THEN** relevant backend tests, `cd platform_web && npm run typecheck && npm run test && npm run build`, `scripts/check-structure.sh`, and `openspec validate complete-platform-web-management-workflows --strict` MUST pass or have documented blockers - -#### Scenario: Browser walkthrough covers edited pages -- **WHEN** frontend management workflows are claimed complete -- **THEN** a browser walkthrough MUST verify users, servers, plugin marketplace, AI providers, and related error/empty states at desktop and narrow widths diff --git a/openspec/changes/complete-platform-web-management-workflows/tasks.md b/openspec/changes/complete-platform-web-management-workflows/tasks.md deleted file mode 100644 index c8b2d7c..0000000 --- a/openspec/changes/complete-platform-web-management-workflows/tasks.md +++ /dev/null @@ -1,65 +0,0 @@ -## 1. API and Safety Inventory - -- [x] 1.1 Inventory current platform APIs for users, server instances, server administrators, plugin marketplace, AI providers, config, logs, run endpoints, and audit events. -- [x] 1.2 Decide and document resource-specific removal semantics for users, server instances, and AI providers: delete, disable, archive, or retire. -- [x] 1.3 Identify missing backend DTOs, validators, repository methods, service methods, and HTTP routes required by the frontend completion workflows. -- [x] 1.4 Confirm no planned request or response shape includes raw AI keys, raw host paths, run credentials, direct socket details, or unrestricted plugin execution fields. - -## 2. Platform API Support - -- [x] 2.1 Add or extend user management APIs for editing identity/contact fields, roles, status, and delete/deactivate behavior. -- [x] 2.2 Add or extend server instance APIs for metadata edits and safe archive/delete behavior while preserving lifecycle validation. -- [x] 2.3 Add or extend AI provider APIs for empty list correctness and delete/retire behavior with secret-reference-only validation. -- [x] 2.4 Add backend tests for accepted and rejected edit/delete/archive/retire workflows and stable JSON errors. -- [x] 2.5 Update platform route/API documentation for newly added management actions. - -## 3. Frontend Contracts and Schemas - -- [x] 3.1 Add frontend API DTO types and client methods for all new user, server, plugin, and AI provider management actions. -- [x] 3.2 Add shared frontend contracts for edit forms, removal confirmations, operation result state, and development-fixture state outside page components. -- [x] 3.3 Add or update frontend schemas for user edit, server metadata edit, server removal, AI provider save, and AI provider retirement requests. -- [x] 3.4 Remove unused static shell demo constants or isolate them as explicit test/development fixtures. - -## 4. User Management Completion - -- [x] 4.1 Replace silent `fallbackUsers` display with API-backed loading, empty, error, and explicitly labeled local-development states. -- [x] 4.2 Add existing-user edit controls for profile/contact fields, roles, and status using typed API requests. -- [x] 4.3 Add user delete/deactivate confirmation flow with persisted result feedback and rejection diagnostics. -- [x] 4.4 Add tests for user empty state, edit success, edit failure, status update, and delete/deactivate behavior. - -## 5. Server Management Completion - -- [x] 5.1 Add server list or detail controls for editable server metadata such as display name and allowed ownership-visible fields. -- [x] 5.2 Add safe server archive/delete flow with state-aware confirmation and platform rejection feedback. -- [x] 5.3 Ensure server administrator add/remove flows refresh assigned administrators and candidates after each operation. -- [x] 5.4 Replace server config fallback behavior with explicit API unavailable state or clearly labeled local-development fixture state. -- [x] 5.5 Add tests for server metadata edit, archive/delete rejection, administrator refresh, and config unavailable state. - -## 6. Plugin Marketplace Completion - -- [x] 6.1 Remove production reliance on `pluginCatalog` fallback data from marketplace list and detail rendering. -- [x] 6.2 Render marketplace API empty and error states with retry and diagnostic context. -- [x] 6.3 Ensure install, enable, and disable actions update list and detail state only from persisted platform responses. -- [x] 6.4 Add tests for marketplace API failure, empty list, detail refresh, disabled fixture actions, and state action feedback. - -## 7. AI Provider Completion - -- [x] 7.1 Fix zero-provider API responses so seed providers are not kept visible after a successful empty list. -- [x] 7.2 Remove optimistic local success for failed save, status, test, model refresh, and delete/retire actions. -- [x] 7.3 Add delete or retire action UI with confirmation, persisted response handling, and reference-safety rejection feedback. -- [x] 7.4 Add tests for empty provider list, create/update failure, status failure, model refresh failure, and delete/retire behavior. - -## 8. Verification - -- [x] 8.1 Run relevant backend tests from `platform/` and record evidence. -- [x] 8.2 Run `cd platform_web && npm run typecheck && npm run test && npm run build` and record evidence. -- [x] 8.3 Run a browser walkthrough covering users, servers, plugin marketplace, AI providers, and empty/error states at desktop and narrow widths. -- [x] 8.4 Run forbidden-fragment checks for raw AI keys, raw host paths, run credentials, and direct socket details in rendered management pages. -- [x] 8.5 Run `scripts/check-structure.sh` and record evidence. -- [x] 8.6 Run `openspec validate complete-platform-web-management-workflows --strict` and record evidence. - -## 9. Management Layout Corrections - -- [x] 9.1 Replace permanent inline create/edit/detail panes on AI provider, user, and plugin marketplace list pages with modal workflows while preserving full-width list surfaces. -- [x] 9.2 Document the ban on permanent right-side or inline split management panes in platform_web Markdown guidance. -- [x] 9.3 Verify AI provider create/edit, user create/edit, and plugin detail workflows in a browser after the modal conversion. diff --git a/openspec/changes/complete-run-build-download-flow/.openspec.yaml b/openspec/changes/complete-run-build-download-flow/.openspec.yaml deleted file mode 100644 index c0a8162..0000000 --- a/openspec/changes/complete-run-build-download-flow/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-21 diff --git a/openspec/changes/complete-run-build-download-flow/design.md b/openspec/changes/complete-run-build-download-flow/design.md deleted file mode 100644 index 00dcaf5..0000000 --- a/openspec/changes/complete-run-build-download-flow/design.md +++ /dev/null @@ -1,66 +0,0 @@ -## Context - -There are two local run checkouts: `/Users/tasia/Desktop/code/browser/run` and `/Users/tasia/Desktop/code/run`. Both point at `git.npc0.com:admin343/run.git`, but neither should be treated as the final generated runtime artifact. The current-directory checkout is an ignored editable source input for convenience and project-size control. Local debug must copy or upload that source into an ignored closed build bucket before any build-capable worker uses it. The sibling checkout has unrelated dirty changes and should not be required by this workflow. - -Platform already queues `distribution.build` jobs and exposes safe browser download routes only after the build artifact is available. The run worker already has a real build path that fetches secret-bearing build input through a leased job channel, copies an approved build source, builds a target executable, packages it with config, uploads it through the artifact channel, and returns `artifact://` as the terminal job result. This change closes the remaining gaps around source snapshotting, closed build bucket scoping, local smoke proof, and concurrency evidence. - -## Goals / Non-Goals - -**Goals:** -- Make local debug use the run checkout under this repository root as editable source input only. -- Snapshot that source into an ignored closed build bucket before the bootstrap worker starts or any generated run distribution is built. -- Build and start the local bootstrap worker from the bucket snapshot instead of `go run`-ing `browser/run` directly. -- Keep run as a separate Git repository and keep browser from tracking run source files. -- Build run packages in plugin/job-scoped isolated directories below the closed bucket so multiple servers for the same plugin cannot overwrite each other's source, config, archive, upload, or result. -- Prove download works after build completion by opening the latest run distribution reference and reading artifact content chunks. -- Report the exact configuration surface needed to run and verify the flow. - -**Non-Goals:** -- Do not move run source into browser's tracked source tree. -- Do not delete or mutate `/Users/tasia/Desktop/code/run`; only stop using it as the default local debug target. -- Do not add arbitrary shell execution, cloud hosting, billing, SaaS marketplace flows, plugin raw credentials, or direct sockets. -- Do not implement production signing/KMS or rollout rings. -- Do not require a live Windows host for local acceptance; local smoke can cross-compile Windows packages and verify the artifact archive. - -## Decisions - -### Decision 1: Local debug separates editable source from build buckets - -`scripts/local-debug-env.sh` will introduce `RUN_SOURCE_DIR` as the editable source checkout, defaulting to `$LOCAL_DEBUG_ROOT_DIR/run`. The legacy `RUN_REPO_DIR` environment variable remains an alias for source selection for compatibility, but it is not the build or execution artifact path. - -The scripts will snapshot `RUN_SOURCE_DIR` into `RUN_BUILD_SOURCE_ROOT`, defaulting under `$LOCAL_DEBUG_ROOT/run/build-buckets/source/current`. Local debug will build `RUN_BOOTSTRAP_BIN` from that snapshot, then start the bootstrap worker binary. Platform-dispatched `distribution.build` jobs will copy from the bucket snapshot into plugin/job workspaces, never from the editable checkout. - -Alternative considered: set `RUN_BUILD_SOURCE_ROOT=$RUN_SOURCE_DIR` and run from `browser/run`. Rejected because it conflates source input with build/runtime artifacts and allows the editable tree to become the effective execution directory. - -### Decision 2: Build workspaces are plugin/job-scoped - -The run worker will create distribution build workspaces under `RUN_WORKSPACE_ROOT/distribution-builds//`. The plugin dimension keeps same-plugin build queues inspectable and ready for per-plugin scheduling, while the job dimension prevents two servers or two idempotency keys from sharing mutable files. Artifact IDs and build job IDs remain Platform-derived and server-scoped. - -Alternative considered: one directory per server. Rejected because multiple builds for the same plugin should queue and isolate by job, not mutate a long-lived per-server build tree. - -### Decision 3: Download proof is mandatory after build success - -Local smoke should keep the existing action availability check, but when `generate-run` is available it must generate a run package, wait for the build job to succeed, open `/run/download`, read content chunks, verify size/checksum metadata, and reject forbidden fragments. A build-capable run endpoint that cannot produce a downloadable artifact is a failing smoke. - -### Decision 4: `/Users/tasia/Desktop/code/run` remains optional - -The sibling checkout is not required for this workflow once local debug snapshots from `browser/run` by default. It can remain for manual comparison or be removed by the user later, but this change will not delete it, mutate it, or depend on it. - -## Risks / Trade-offs - -- [Risk] Running `go mod download` during local smoke may need network if caches are cold. Mitigation: tests exercise build logic without network where possible; full smoke may require pre-cached modules or an approved network-capable environment. -- [Risk] `RUN_MAX_JOBS>1` does not by itself make the current worker execute multiple jobs at once if its main loop is synchronous. Mitigation: workspace isolation is still required and tested directly; worker parallelism can remain a future scheduler improvement. -- [Risk] Cross-compiling Windows packages on macOS validates packaging but not Windows service activation. Mitigation: local acceptance checks archive content and platform artifact flow; OS-native activation remains a target-environment proof. -- [Risk] Two run checkouts can confuse operators. Mitigation: local debug prints the editable source, bucket snapshot, bootstrap binary, and final handoff documents the optional sibling checkout clearly. - -## Migration Plan - -1. Update local debug defaults to snapshot `browser/run` into `.local-debug` and pass the snapshot as `RUN_BUILD_SOURCE_ROOT`. -2. Harden run distribution build workspace naming and add concurrency/isolation tests in the run checkout. -3. Extend local smoke to download and checksum the generated run artifact. -4. Run OpenSpec validation, structure checks, focused Platform tests, focused run tests, and smoke/script syntax checks. -5. Rollback by pointing `RUN_SOURCE_DIR`/`RUN_REPO_DIR` at another checkout and disabling the new smoke assertions; generated artifacts remain ordinary platform artifacts. - -## Open Questions - -- Whether to keep `/Users/tasia/Desktop/code/run` as a personal scratch checkout is an operator workspace decision; it is not required by local debug after this change. diff --git a/openspec/changes/complete-run-build-download-flow/proposal.md b/openspec/changes/complete-run-build-download-flow/proposal.md deleted file mode 100644 index 0b93f83..0000000 --- a/openspec/changes/complete-run-build-download-flow/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The run build/download flow is split across Platform, platform_web, local debug scripts, and the independent run checkout. The editable run source may live at `browser/run` for convenience, but that directory must be treated as source input only. Local debug must snapshot or upload that source into an ignored, closed build bucket, let Platform dispatch `distribution.build`, build from the bucket, and prove the generated artifact can be downloaded and executed/updated without using the editable source tree as the runtime artifact. - -## What Changes - -- Treat `run/` under this repository root as the editable independent run source checkout for local debug, while keeping it ignored by the browser repository and still owned by `git@git.npc0.com:admin343/run.git`. -- Snapshot the editable run source into a closed ignored local build bucket before starting build-capable local debug flows; `RUN_BUILD_SOURCE_ROOT` must point at that bucket snapshot, not the editable checkout. -- Build the local bootstrap run worker from the bucket snapshot instead of executing `browser/run` in place; generated run packages remain Platform-dispatched `distribution.build` artifacts. -- Harden run distribution build workspaces so build output is isolated by plugin and job, not by server-wide mutable directories or editable source folders. -- Add tests that prove two servers for the same plugin can generate separate run distributions without artifact/config/key/result cross-talk. -- Upgrade local smoke proof so `scum-alpha` run generation is mandatory when the run endpoint advertises `distribution.build`, then download the generated artifact and verify safe metadata. -- Document every configuration value operators must provide or may tune for local debug and run distribution builds. - -## Capabilities - -### New Capabilities -- `run-build-download-flow`: Covers local-debug source snapshotting, closed build buckets, plugin/job-scoped run package builds, browser-safe run artifact downloads, and same-plugin multi-server build isolation. - -### Modified Capabilities -- `run-distribution-and-client-managers`: Completed implementation must use the current-directory run checkout only as source input and prove generated run artifacts are downloadable. -- `artifact-transfer-channel`: Completed implementation must prove browser downloads and run artifact uploads remain chunked, checksummed, and free of leaked host paths or secrets. - -## Impact - -- Affected roots: `scripts/`, `platform/`, `platform_web/`, and the ignored independent checkout at `run/`. -- Affected local configuration: `RUN_SOURCE_DIR`/legacy `RUN_REPO_DIR`, `RUN_BUILD_BUCKET_ROOT`, `RUN_BUILD_SOURCE_ROOT`, `RUN_BOOTSTRAP_BIN`, `RUN_WORKSPACE_ROOT`, `RUN_SPOOL_ROOT`, `RUN_MAX_JOBS`, `RUN_PLATFORM_URL`, `RUN_ENDPOINT_ID`, platform storage/artifact paths, and bootstrap credentials. -- Verification requires structure checks, Platform tests, run tests from `run/`, frontend tests where touched, OpenSpec validation, and local debug smoke evidence. diff --git a/openspec/changes/complete-run-build-download-flow/specs/run-build-download-flow/spec.md b/openspec/changes/complete-run-build-download-flow/specs/run-build-download-flow/spec.md deleted file mode 100644 index 4f06003..0000000 --- a/openspec/changes/complete-run-build-download-flow/specs/run-build-download-flow/spec.md +++ /dev/null @@ -1,45 +0,0 @@ -## ADDED Requirements - -### Requirement: Local debug snapshots run source into a closed build bucket -The system SHALL default local debug run source input to the independent `run/` checkout under the browser repository root when that checkout exists, SHALL keep that checkout outside browser Git tracking, and SHALL snapshot that source into an ignored closed build bucket before build-capable local debug execution. - -#### Scenario: Default run source snapshot resolution -- **WHEN** local debug scripts start the run worker without an explicit `RUN_SOURCE_DIR` or legacy `RUN_REPO_DIR` -- **THEN** they use `/run` as editable source input, copy it into `RUN_BUILD_SOURCE_ROOT` under an ignored local build bucket, and start the local bootstrap worker from a binary built from that snapshot - -#### Scenario: Sibling checkout is optional -- **WHEN** `/Users/tasia/Desktop/code/run` exists or does not exist -- **THEN** local debug behavior does not depend on that sibling checkout unless `RUN_SOURCE_DIR` or legacy `RUN_REPO_DIR` is explicitly overridden - -### Requirement: Run distribution builds are plugin and job isolated -The run worker SHALL build generated run and client-manager distributions in a closed workspace scoped by plugin ID and job ID, and SHALL NOT use a mutable per-server build directory or editable source checkout for source, config, archive, upload, or terminal result state. - -#### Scenario: Same plugin builds for multiple servers -- **WHEN** two `distribution.build` jobs for different servers but the same plugin run concurrently or back-to-back -- **THEN** each job writes to a distinct plugin/job workspace and uploads only its assigned artifact ID - -#### Scenario: Build package config isolation -- **WHEN** a run distribution archive is produced -- **THEN** its config belongs to the job's server instance, plugin, endpoint, target, key generation, and auth key without leaking those secret values through API or UI responses - -### Requirement: Generated run artifacts are downloadable after build success -The system SHALL make a generated run distribution downloadable only after the build job succeeds and the referenced artifact is available, checksummed, and owned by the build job. - -#### Scenario: Download latest generated run -- **WHEN** a server has an available run distribution -- **THEN** `/api/v1/server-instances/{id}/run/download` returns a browser-safe artifact reference and `/api/v1/artifacts/{artifactId}/content` returns bounded byte ranges with checksum headers - -#### Scenario: No synthetic success before artifact upload -- **WHEN** a distribution build result is reported before the artifact upload is available -- **THEN** Platform rejects the terminal success and the distribution remains non-downloadable - -### Requirement: Local smoke proves run build and download -The local debug smoke SHALL fail when a build-capable run endpoint cannot complete run generation and artifact download for the SCUM fixture. - -#### Scenario: Build-capable endpoint smoke -- **WHEN** `scum-alpha` exposes `generate-run` as available -- **THEN** smoke generates the run package, waits for the build job to succeed, opens the latest download reference, reads the artifact content, verifies size/checksum metadata, and rejects forbidden fragments - -#### Scenario: Build-unavailable endpoint smoke -- **WHEN** the endpoint does not advertise `distribution.build` -- **THEN** smoke records that generation is unavailable without claiming a fake run artifact was built diff --git a/openspec/changes/complete-run-build-download-flow/tasks.md b/openspec/changes/complete-run-build-download-flow/tasks.md deleted file mode 100644 index 508e484..0000000 --- a/openspec/changes/complete-run-build-download-flow/tasks.md +++ /dev/null @@ -1,33 +0,0 @@ -## Prompt Boundaries - -- [x] 0.1 Positive prompt (正向提示词): complete the first-party 服务器管理 run generation/download flow so `game.scum` servers can build, download, and safely reuse generated run artifacts with per-server keys and auditable jobs. -- [x] 0.2 Directional prompt (方向提示词): preserve Platform-owned authorization, current platform_web visual style, `browser/run` as an ignored independent checkout, plugin/job-scoped run build workspaces, and verification through `scripts/check-structure.sh`, focused Go tests, OpenSpec validation, and local debug smoke. -- [x] 0.3 Boundary prompt (任务边界): do not add billing, cloud host sales, arbitrary shell execution, raw key exposure, direct plugin transports, tracked browser/run source, or destructive changes to `/Users/tasia/Desktop/code/run`. - -## 1. Local Debug Source Snapshot And Build Bucket - -- [x] 1.1 Add `RUN_SOURCE_DIR` (legacy alias `RUN_REPO_DIR`) for editable source input and keep `/Users/tasia/Desktop/code/run` optional. -- [x] 1.2 Snapshot `RUN_SOURCE_DIR` into `RUN_BUILD_SOURCE_ROOT` under `RUN_BUILD_BUCKET_ROOT` before local debug build-capable execution. -- [x] 1.3 Build and start the local bootstrap run worker from the bucket snapshot instead of directly executing `browser/run`. -- [x] 1.4 Make smoke evidence include the resolved source, bucket, snapshot, bootstrap binary, workspace, spool, and queue configuration. - -## 2. Run Build Isolation - -- [x] 2.1 Scope run distribution build workspaces by plugin ID and job ID. -- [x] 2.2 Add run tests proving two same-plugin server builds produce distinct workspaces, artifact IDs, and package configs. -- [x] 2.3 Confirm generated archive packaging still includes the executable and config for Linux/tar.gz and Windows/zip targets where locally testable. - -## 3. Download And Smoke Proof - -- [x] 3.1 Extend local smoke to open the latest run download reference after build success. -- [x] 3.2 Read generated run artifact content in chunks and verify total size plus checksum metadata. -- [x] 3.3 Keep forbidden-fragment checks over distribution, job, artifact, download reference, and chunk evidence. - -## 4. Verification - -- [x] 4.1 Run `openspec validate complete-run-build-download-flow --strict`. -- [x] 4.2 Run `scripts/check-structure.sh`. -- [x] 4.3 Run focused Platform distribution/artifact tests. -- [x] 4.4 Run focused `run/` distribution build tests. -- [x] 4.5 Run `bash -n scripts/local-debug-smoke.sh scripts/local-debug-start.sh scripts/local-debug-env.sh`. -- [x] 4.6 Run local debug smoke or record any environment blocker precisely. diff --git a/openspec/changes/complete-scum-deployment-lifecycle/.openspec.yaml b/openspec/changes/complete-scum-deployment-lifecycle/.openspec.yaml deleted file mode 100644 index 3a03821..0000000 --- a/openspec/changes/complete-scum-deployment-lifecycle/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-25 diff --git a/openspec/changes/complete-scum-deployment-lifecycle/design.md b/openspec/changes/complete-scum-deployment-lifecycle/design.md deleted file mode 100644 index e500698..0000000 --- a/openspec/changes/complete-scum-deployment-lifecycle/design.md +++ /dev/null @@ -1,63 +0,0 @@ -## Context - -`ServerDeploymentDefinition` currently carries generic mode, paths, commands, and create inputs. `dispatchLifecycleJob` forwards that definition, while `projectLifecycleJobResult` only projects a generic ready/running/stopped state. The SCUM manifest has a SteamCMD app id and create fields, but no contract tying those fields to a real SCUM config file or verification evidence. The Run executor is a separate repository, so Platform must define a complete, bounded wire contract without pretending to implement host execution locally. - -## Goals / Non-Goals - -**Goals:** - -- Make install and adoption separate operations with authoritative Run preflight. -- Freeze a SCUM template into each leased job so plugin changes cannot mutate an in-flight deployment. -- Return safe, structured scan/config/verification projections that the future UI can render. -- Keep all raw paths, commands, credentials, and sockets inside Run; public Platform views expose only configured flags, logical keys, facts, and stable error codes. -- Preserve idempotency, retry, cancellation, and channel separation already used by lifecycle jobs. - -**Non-Goals:** - -- Implementing SteamCMD, SCUM binaries, Windows process control, or filesystem scanning in this repository. -- Allowing arbitrary plugin commands, arbitrary config paths, or direct browser-to-Run access. -- Replacing the existing generic lifecycle protocol for non-SCUM plugins. - -## Decisions - -### 1. Add a typed SCUM deployment template to runtime profiles - -Add `serverDeployments` to `GamePluginRuntimeProfiles`. A profile contains a stable key/version, target OS/architecture, Steam app id, executable marker, install root logical key, config file logical key, field-to-config mappings, discovery markers, and verification checks. The SCUM manifest declares one Windows template for app `3792580` and explicit mappings for `serverName`, `gamePort`, `queryPort`, and `maxPlayers`. - -This is preferred over teaching Run to infer SCUM behavior from `createInputs` or free-form action JSON. Inference would make the same directory mean different things in different versions and would be impossible to audit. - -### 2. Freeze the template into the leased assignment - -`JobExecutionInput` gains a `ServerDeploymentPlan` containing `schemaVersion`, operation (`install` or `adopt`), template identity, and bounded template data. The existing protected `ServerDeploymentDefinition` remains the source of operator paths/commands. DTO conversion exposes the plan only in `RunJobAssignmentResponse.executionInput`, never in browser-facing job or server responses. - -### 3. Return bounded lifecycle evidence through the terminal result - -`JobExecutionResult` gains an optional `ServerDeploymentEvidence` object. It records preflight checks, discovery facts (executable/version/ports/config/log markers as logical names), config mapping outcomes, verification checks, and a stable failure code. Values are bounded and path/command redacted. Platform persists a safe projection on `ServerInstance` and only projects `ready` when the required verification checks pass. - -This uses the existing job result channel so control, logs, and artifacts remain independent. Large scan output or logs use existing artifact/log channels and are referenced, not inlined. - -### 4. Use explicit install/adopt operation semantics - -Guided install dispatches `install`: preflight → SteamCMD install → config materialization → launch/status health checks. Existing-server adoption dispatches `adopt`: preflight → scan → optional reviewed config mapping → status health checks. A failed scan never silently falls back to install, and a failed verification projects `failed` with an actionable code. - -### 5. Persist safe projection, not host evidence - -Add a `ServerDeploymentProjection` to `ServerInstance` with state, operation, template key/version, safe discovered facts, mapping status, verification status, failure code, and timestamps. No raw path, command, credential, process id, or socket appears in the projection or DTO. - -## Risks / Trade-offs - -- [Run is in an independent repository] -> Version the contract and add fixture-based Platform tests; mark real execution dependent on the coordinated Run implementation. -- [Existing installations have different config layouts] -> Discovery reports marker confidence and config mapping status; adoption requires explicit operator approval when mapping is incomplete. -- [Evidence can grow large] -> Enforce bounded counts/lengths and use artifact references for detailed reports. -- [Old Run versions cannot understand the plan] -> Gate dispatch on a new capability string and reject before changing server state. - -## Migration Plan - -1. Ship additive domain/DTO/protocol types and manifest declarations; old plugins continue using the legacy lifecycle path. -2. Gate SCUM controlled install/adopt on `deployment.scum.v1` capability. Existing generic `deployment.plan.v1` remains valid for other plugins. -3. Coordinate the independent Run implementation and enable the capability only after its contract tests pass. -4. Update the frontend to render the persisted projection and evidence; rollback leaves legacy deployments untouched. - -## Open Questions - -- The exact SCUM config filename can vary by distribution. The first template uses the known `ServerSettings.ini` logical marker and allows Run to report a discovered alternate marker without exposing its absolute path; a later template version can add mappings without changing the Platform projection. diff --git a/openspec/changes/complete-scum-deployment-lifecycle/proposal.md b/openspec/changes/complete-scum-deployment-lifecycle/proposal.md deleted file mode 100644 index 4217ee9..0000000 --- a/openspec/changes/complete-scum-deployment-lifecycle/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The current SCUM workflow treats a directory and a few generic fields as if they were a complete installation. That cannot distinguish a new SteamCMD install from adoption of an existing server, cannot prove that SCUM configuration was materialized, and marks a job ready without a game-specific health check. - -## What Changes - -- Add a versioned SCUM deployment template that declares the Steam app, supported target, executable markers, config file mappings, discovery markers, and post-install verification checks. -- Extend the Platform-to-Run job contract with bounded preflight, install/scan projection, config mapping, and verification evidence; keep protected paths and commands Run-local. -- Persist a safe deployment projection for operators: preflight state, discovered runtime facts, config mapping status, verification state, and stable failure codes without raw host paths or command text. -- Make SCUM install and adopt operations explicit and idempotent. A new install must run install and configure phases; adoption must scan before any managed write. -- Add contract tests and manifest validation for the complete SCUM lifecycle. The UI will consume these projections after the backend contract is in place. - -## Capabilities - -### New Capabilities - -- `scum-deployment-lifecycle`: Game-specific controlled install/adopt plans, scan projections, config mappings, and post-install verification. - -### Modified Capabilities - -- `run-job-channel`: Leased deployment jobs carry a validated SCUM template and return bounded lifecycle evidence. - -## Impact - -- Affects `platform/domain`, `platform/dto`, `platform/service`, `platform/validator`, and `platform/protocol`. -- Affects `plugins/manifests/game-plugin.manifest.schema.json`, SCUM manifest assets, and plugin validation tests. -- Requires a coordinated implementation in the independent Run repository before real machines can execute the new template; this repository provides the authoritative contract and safe projection. -- Does not add billing, cloud host sales, direct SSH, raw host paths to browser/plugin responses, or a Run source tree here. diff --git a/openspec/changes/complete-scum-deployment-lifecycle/specs/scum-deployment-lifecycle/spec.md b/openspec/changes/complete-scum-deployment-lifecycle/specs/scum-deployment-lifecycle/spec.md deleted file mode 100644 index 15e15ed..0000000 --- a/openspec/changes/complete-scum-deployment-lifecycle/specs/scum-deployment-lifecycle/spec.md +++ /dev/null @@ -1,59 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM deployments use a versioned game template -The Platform SHALL register a versioned SCUM server deployment template that declares the Steam app id, compatible target, executable marker, install root logical key, config file logical key, supported field mappings, discovery markers, and required verification checks. A leased install or adoption job MUST freeze the template version used for that job. - -#### Scenario: Guided SCUM install template -- **WHEN** an operator creates a SCUM server with guided installation on a compatible Windows Run node -- **THEN** Platform dispatches an `install` job with the frozen SCUM template, Steam app `3792580`, and mappings for the declared SCUM fields -- **AND** the assignment contains no raw browser credential or direct socket - -#### Scenario: Unsupported Run is rejected -- **WHEN** the selected Run node does not advertise `deployment.scum.v1` -- **THEN** Platform rejects the deployment before changing the instance to installing -- **AND** the response identifies the missing capability without exposing host details - -### Requirement: New install and existing-server adoption are distinct -The Platform SHALL dispatch guided installation and existing-server adoption as different operations. Installation SHALL perform preflight, SteamCMD install, configuration materialization, and health verification. Adoption SHALL perform preflight and discovery first, and SHALL NOT reinstall or overwrite existing configuration without an explicit approved mapping request. - -#### Scenario: Adoption discovers an existing server -- **WHEN** an operator chooses existing-server adoption -- **THEN** Run scans the selected logical server root and returns bounded executable, version, port, config-marker, and log-marker facts -- **AND** Platform persists those facts as a safe deployment projection - -#### Scenario: Adoption scan fails -- **WHEN** the scan cannot identify a compatible SCUM executable or required marker -- **THEN** the instance remains failed or draft with a stable failure code -- **AND** Platform does not silently switch to install - -### Requirement: SCUM configuration mappings are explicit and reviewable -The SCUM template SHALL map only declared create fields to known logical configuration keys. Run SHALL report each mapping as `applied`, `unchanged`, `skipped`, or `failed` with a bounded reason code. Platform SHALL require successful required mappings before reporting an install as ready. - -#### Scenario: Materialize SCUM settings -- **WHEN** a new SCUM install completes SteamCMD setup with valid inputs -- **THEN** Run applies `serverName`, `gamePort`, `queryPort`, and `maxPlayers` through the frozen mappings -- **AND** the evidence reports the mapping outcomes without returning the absolute config path - -#### Scenario: Unsupported field is submitted -- **WHEN** a create request includes a field not present in the template mapping -- **THEN** Platform rejects the request before dispatch - -### Requirement: Installation completion requires verification evidence -The Platform SHALL accept SCUM installation as ready only when Run returns successful required checks for executable presence/version, configured ports, config readability, and process health. A terminal success without required evidence SHALL be rejected as an invalid result. - -#### Scenario: Health verification succeeds -- **WHEN** Run reports all required SCUM checks as passed -- **THEN** Platform projects the instance to ready after install, or running after start -- **AND** the deployment projection records the verification timestamp and template version - -#### Scenario: Verification fails -- **WHEN** any required check fails -- **THEN** Platform projects the job as failed with a stable error code and keeps the raw diagnostic local to Run - -### Requirement: Deployment evidence is safe for browser projection -Public server, job, marketplace, and plugin bridge responses SHALL expose only bounded logical facts and configured/reviewable state. They MUST NOT expose raw host paths, command text, credentials, process ids, or direct sockets. - -#### Scenario: Operator reads deployment status -- **WHEN** an operator opens a SCUM deployment status view -- **THEN** the response includes operation, template version, discovery/mapping/verification states, and safe failure code -- **AND** it omits the supplied server root, working directory, install/start/stop commands, and any secret material diff --git a/openspec/changes/complete-scum-deployment-lifecycle/tasks.md b/openspec/changes/complete-scum-deployment-lifecycle/tasks.md deleted file mode 100644 index b3afd2a..0000000 --- a/openspec/changes/complete-scum-deployment-lifecycle/tasks.md +++ /dev/null @@ -1,23 +0,0 @@ -## 1. Contract and domain model - -- [x] 1.1 Add typed SCUM deployment templates, config mappings, discovery markers, verification checks, and safe deployment projections to `platform/domain`. -- [x] 1.2 Extend Run assignment/result DTOs and conversion with versioned deployment plans and bounded evidence; keep public projections redacted. -- [x] 1.3 Add validator rules and capability gating for `deployment.scum.v1`, bounded evidence, operation-specific requirements, and required verification checks. - -## 2. Platform lifecycle behavior - -- [x] 2.1 Freeze the selected SCUM template into install/adopt jobs and distinguish `install` from `adopt` dispatch semantics. -- [x] 2.2 Persist Run evidence into the safe server deployment projection and gate ready/running state on required verification. -- [x] 2.3 Add server deployment status DTO/API projection for preflight, scan, mapping, verification, and stable failure codes. - -## 3. SCUM plugin assets - -- [x] 3.1 Extend the manifest schema and domain conversion for `serverDeployments`. -- [x] 3.2 Declare the SCUM Windows SteamCMD template, executable/config markers, field mappings, adoption scan markers, and post-install checks in the first-party manifest. -- [x] 3.3 Add manifest validation and fixture tests for the SCUM template and unsafe-value rejection. - -## 4. Verification and handoff - -- [x] 4.1 Add Platform unit/contract tests covering install/adopt semantics, target/capability rejection, mapping validation, evidence projection, and redaction. -- [x] 4.2 Add protocol documentation and Run coordination notes for the independent executor implementation. -- [x] 4.3 Run Go tests, plugin typecheck/test/manifest validation, `scripts/check-structure.sh`, and `openspec validate complete-scum-deployment-lifecycle --strict`. diff --git a/openspec/changes/enrich-platform-operations-console/.openspec.yaml b/openspec/changes/enrich-platform-operations-console/.openspec.yaml deleted file mode 100644 index 0bd76e6..0000000 --- a/openspec/changes/enrich-platform-operations-console/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-18 diff --git a/openspec/changes/enrich-platform-operations-console/design.md b/openspec/changes/enrich-platform-operations-console/design.md deleted file mode 100644 index 3dc94c9..0000000 --- a/openspec/changes/enrich-platform-operations-console/design.md +++ /dev/null @@ -1,92 +0,0 @@ -## Context - -The console already has five required first-party areas, typed Platform API clients, role-scoped routes, long-running job polling, operation tracking, and the black-mecha/magical-girl theme system. The remaining problem is cohesion and truthfulness: the overview hides some API failures as empty collections, page mutations use inconsistent busy/confirm/retry behavior, and operators lack a compact cross-page view of active and failed work. - -This change stays inside `platform_web/` and consumes existing Platform API projections. It does not make the browser a source of truth for jobs, resource state, permissions, or secrets. - -## Goals / Non-Goals - -**Goals:** - -- Make loading, empty, unavailable, stale, active, succeeded, and failed states visually and semantically distinct. -- Give operators an actionable overview of server health, Run endpoint safe status, active/failed jobs, AI provider availability, resource pressure, and recent audit signals. -- Standardize mutation behavior around permission gating, confirmation where state is disruptive, action-level busy state, persisted response handling, and retry after failure. -- Keep first-party list pages full-width and dense while retaining modal/detail-route workflows. -- Preserve the theme and responsive contracts at desktop and 390px. -- Recover narrow-screen vertical space by moving the existing ordered text navigation into an off-canvas sidebar with a small theme-aware left-edge handle and bounded swipe gesture. - -**Non-Goals:** - -- No new Platform or Run endpoint, persistence model, permission, or job state machine. -- No raw secret/key/token/path/PID/socket/credential/DSN/RCON projection. -- No billing, cloud hosting sales, provider marketplace, fleet orchestration, arbitrary shell, or direct browser-to-Run channel. -- No route, sidebar order, theme palette, or global particle architecture replacement. - -## Decisions - -### Decision 1: Aggregate existing safe APIs in the browser without inventing platform health - -The overview will fetch server instances, safe Run endpoint projections, jobs, metrics, platform usage, AI providers, and audit events as independent modules. Each module keeps its own state and refresh timestamp. Failed modules remain visibly unavailable and are excluded from healthy/empty conclusions. - -Alternative considered: add one new dashboard API. Rejected because current APIs already provide the bounded projections needed, and this change does not need a new backend contract. - -### Decision 2: Derive operational summaries in shared pure contracts - -Job buckets, attention signals, server list sorting/filtering, provider summaries, and safe operation labels will live outside page components. Pages render derived view contracts and tests can lock classification without constructing full page trees. - -Alternative considered: calculate every summary inline. Rejected because the same terminal/active/failure classification is needed by the overview, server page, and global operation tray. - -### Decision 3: Platform responses remain authoritative - -Mutations only update resource state from a successful Platform response or a subsequent refresh. Long-running operations use real job IDs and job polling. A rejected request keeps the previous resource state, preserves the failure message, and exposes retry where the same request remains valid. - -Alternative considered: optimistic state transitions. Rejected because plugin/provider/server operations can be rejected by ownership, lifecycle, dependency, or reference constraints. - -### Decision 4: Disruptive state changes use a shared confirmation contract - -Plugin install/enable/disable, AI provider enable/disable/retire, user deactivation, and server destructive actions require resource-specific confirmation. Dialogs close on Escape or cancellation, preserve focus behavior, and disable duplicate submission while busy. - -Alternative considered: confirm only destructive deletes. Rejected because enable/disable and install actions can interrupt running capabilities or create durable work. - -### Decision 5: Capabilities control commands, routes control discovery - -The existing route capability mapping remains the navigation authority. Within an allowed page, mutation controls check the matching session capability and render a clear read-only state when missing. The browser does not elevate access and still handles Platform 401/403 as authoritative rejection. - -Alternative considered: hide every unauthorized control. Rejected because operators benefit from understanding why a page is read-only; safety-critical commands remain unavailable. - -### Decision 6: Extend the existing theme system with shared operational primitives - -New pulse rows, module status headers, compact job rows, and the operation tray use shared classes appended to `theme/base.css`, `var(...)` tokens, existing radius limits, and theme-specific material variables. Global decoration remains exclusively in `MagicalParticleLayer`. - -Alternative considered: page-local cards and effects. Rejected because they would fragment theme switching and violate the repository style contract. - -### Decision 7: Use an off-canvas text sidebar on narrow screens - -At 760px and below, the sidebar leaves normal document flow and stays closed until the operator activates the left-edge handle or completes a rightward swipe that begins at the viewport edge. The drawer preserves the desktop route order, icons, Chinese labels, account access, and session operations; backdrop click, Escape, route selection, and a leftward drawer swipe close it. - -Alternative considered: keep the horizontal icon rail and account strip above every page. Rejected because it consumes scarce vertical space, hides route labels, and makes repeated mobile operations slower to scan. - -## Risks / Trade-offs - -- [Risk] Independent overview requests can complete out of order. Mitigation: refresh callbacks update only their module and use explicit loading/ready/error states. -- [Risk] More status rows can overload narrow layouts. Mitigation: collapse multi-column regions to a single ordered flow at 390px and keep command rows bounded. -- [Risk] Horizontal content gestures can open navigation accidentally. Mitigation: require the opening swipe to start within the left 28px edge and travel at least 56px, while drawer-closing swipes use the inverse threshold. -- [Risk] In-session operation history is not durable. Mitigation: label it as current-session request feedback; durable job and audit history continues to come from Platform APIs. -- [Risk] Existing item 8 edits overlap `base.css` and API types. Mitigation: reuse its current worktree state, append narrowly scoped classes, and avoid reverting or rewriting lifecycle code. -- [Risk] Action confirmation can add friction. Mitigation: require it only for persistent or disruptive operations; refresh and navigation remain immediate. - -## Migration Plan - -1. Add shared derived contracts and tests. -2. Add shared operation tray and accessible dialog behavior. -3. Update each first-party page while keeping existing API methods and routes. -4. Add shared theme styles and responsive rules. -5. Run frontend tests/typecheck/build, structure validation, strict OpenSpec validation, and browser acceptance in both themes and at 390px. - -Rollback is frontend-only: remove the new shared components/contracts and restore the previous page render paths. No persisted data migration is required. - -## Open Questions - -- Resolved: this change uses existing APIs and does not add a dashboard endpoint. -- Resolved: the operation tray is session-scoped feedback; Platform jobs and audit events remain durable truth. -- Resolved: development fixtures may remain in tests, but normal page rendering does not silently substitute them for failed APIs. diff --git a/openspec/changes/enrich-platform-operations-console/proposal.md b/openspec/changes/enrich-platform-operations-console/proposal.md deleted file mode 100644 index 8bcb1ab..0000000 --- a/openspec/changes/enrich-platform-operations-console/proposal.md +++ /dev/null @@ -1,31 +0,0 @@ -## Why - -The first-party console has real management APIs and complete resource workflows, but operators still have to infer platform health across pages, some API failures are rendered as empty success states, and several state-changing actions lack a consistent confirm, progress, and recovery cycle. The next console iteration must turn the existing pages into a cohesive operations workspace without weakening permission or secret boundaries. - -## What Changes - -- Add a permission-aware operations overview that distinguishes live, delayed, failed, empty, and unavailable API modules and surfaces active or failed jobs, endpoint health, resource pressure, and actionable navigation. -- Deepen server management with explicit telemetry availability, job-state summaries, deterministic refresh behavior, and recovery-oriented feedback while retaining real job polling for long-running work. -- Deepen plugin marketplace operations with permission gating, confirmation for state changes, persisted-response refresh, action progress, and retryable failures. -- Remove normal-page user fixture fallbacks and add API-backed search, role/status filtering, operational summaries, retry, and mutation feedback. -- Deepen AI provider management with retryable loading, action-level busy states, confirmation for enable/disable/retire operations, saved-response refresh, and safe configuration presence rather than raw key disclosure. -- Add a compact global operation tray for in-session API mutations, including pending, succeeded, and failed records with diagnostic context and page-safe target labels. -- Replace the narrow-screen top navigation stack with a theme-aware off-canvas sidebar that keeps Chinese labels visible, opens from a bounded left-edge control or rightward edge swipe, and closes without consuming page height. -- Preserve the existing black-mecha and magical-girl crystal-moonlight theme system, shared surfaces, route order, and `MagicalParticleLayer` ownership. -- Keep browser and plugin projections free of raw keys, tokens, secret values, host paths, PIDs, sockets, credentials, DSNs, RCON data, and direct Run endpoints. - -## Capabilities - -### New Capabilities - -- `platform-operations-console-enrichment`: Permission-aware, API-backed operations views and mutation recovery workflows across overview, servers, plugins, users, and AI providers. - -### Modified Capabilities - -None. - -## Impact - -- Affects `platform_web/` contracts, shared components, operation tracking, first-party pages, theme styles, tests, and browser acceptance coverage. -- Uses existing Platform APIs for server instances, Run endpoint safe projections, jobs, metrics, audit events, plugins, users, and AI providers; no new direct Run or secret-bearing browser contract is introduced. -- Does not add billing, cloud host sales, provider marketplaces, arbitrary remote execution, or unrelated SaaS features. diff --git a/openspec/changes/enrich-platform-operations-console/specs/platform-operations-console-enrichment/spec.md b/openspec/changes/enrich-platform-operations-console/specs/platform-operations-console-enrichment/spec.md deleted file mode 100644 index 23b6e85..0000000 --- a/openspec/changes/enrich-platform-operations-console/specs/platform-operations-console-enrichment/spec.md +++ /dev/null @@ -1,124 +0,0 @@ -## ADDED Requirements - -### Requirement: Permission-aware operations overview -The console SHALL provide an overview that derives server, Run endpoint, job, resource, AI provider, and audit summaries from Platform API responses permitted for the active session. - -#### Scenario: Operator opens overview with available APIs -- **WHEN** the active session can read the overview and the required Platform APIs return data -- **THEN** the console shows real counts, resource measurements, active and failed job summaries, endpoint health, and actionable signals derived from those responses - -#### Scenario: Session lacks a management capability -- **WHEN** a session can view an operational resource but lacks the capability required to mutate it -- **THEN** the console presents a read-only explanation and does not dispatch the mutation - -### Requirement: Independent module truth and recovery -Each overview and management data module MUST distinguish loading, ready-empty, ready-data, and unavailable states, and MUST provide scoped retry without treating an API failure as an empty or healthy result. - -#### Scenario: Audit API fails while core overview succeeds -- **WHEN** server and job APIs succeed but the audit API fails -- **THEN** core health remains visible, audit signals are marked unavailable, and the page does not claim that there are no recent audit signals - -#### Scenario: Failed module is retried -- **WHEN** an operator retries an unavailable module and its API succeeds -- **THEN** only that module transitions to ready data and its refresh timestamp is updated - -### Requirement: Real job and mutation progress -Long-running work MUST use Platform job identifiers and job states, while immediate mutations MUST remain pending until the Platform response returns and MUST never use timers or optimistic local success. - -#### Scenario: Long-running server operation is queued -- **WHEN** a server operation returns a job identifier -- **THEN** the console polls the Platform job projection and shows queued, claimed, running, retrying, terminal success, or terminal failure from that projection - -#### Scenario: Mutation is rejected -- **WHEN** the Platform rejects a plugin, user, server, or AI provider mutation -- **THEN** the previous resource state remains visible and the console shows the rejection with a retry or corrective action when applicable - -### Requirement: Confirmed disruptive operations -Persistent or disruptive resource state changes MUST require resource-specific confirmation and MUST prevent duplicate submission while a request is pending. - -#### Scenario: Plugin state change is confirmed -- **WHEN** an authorized operator chooses install, enable, or disable and confirms the named plugin action -- **THEN** the console dispatches one Platform request, disables duplicate confirmation, and refreshes state from the persisted response - -#### Scenario: Confirmation is cancelled -- **WHEN** an operator cancels a confirmation or presses Escape before submission -- **THEN** no mutation is dispatched and the current resource state is preserved - -### Requirement: Server operations workspace -Server management SHALL expose API telemetry availability, deterministic search/filter/sort, active and failed job counts, and recovery-oriented navigation without covering or reflowing server-card operational data. - -#### Scenario: Metrics are unavailable -- **WHEN** the server list succeeds but metrics loading fails -- **THEN** server cards remain available, metrics are labeled unavailable rather than zero, and a scoped metrics retry is offered - -#### Scenario: Server has active or failed jobs -- **WHEN** Platform jobs reference a visible server -- **THEN** the server working surface shows the real active or failed job count and lets the operator open the server detail or job progress workflow - -### Requirement: Plugin marketplace operations -The plugin marketplace SHALL use Platform list/detail/state responses, permission-gated commands, confirmation, action progress, and failure recovery without production fallback data. - -#### Scenario: Plugin state response succeeds -- **WHEN** the Platform accepts a confirmed plugin state action -- **THEN** list and detail views update from the returned or refreshed Platform plugin state and the action result is traceable - -#### Scenario: Plugin detail refresh fails -- **WHEN** the selected plugin summary exists but its detail API fails -- **THEN** the summary remains visible, detail is marked unavailable, and the operator can retry detail loading without reloading the page - -### Requirement: User management operations -User management SHALL render only Platform API data in normal operation and SHALL provide search, role/status filtering, create/edit/status/deactivate feedback, and API-scoped retry. - -#### Scenario: User API fails in development -- **WHEN** the normal user list API fails in a development build -- **THEN** the page shows an unavailable state and does not replace the result with actionable sample users - -#### Scenario: User filters are applied -- **WHEN** an authorized operator enters a query or selects role/status filters -- **THEN** the full-width user working surface shows only matching Platform users and keeps create/edit flows in dialogs - -### Requirement: AI provider operations -AI provider management SHALL provide scoped list retry, action-level busy states, confirmed status/retirement changes, persisted-response refresh, and safe key configuration presence. - -#### Scenario: Provider load is retried -- **WHEN** the provider list fails and the operator retries -- **THEN** the page calls the provider list API again without a full browser reload and renders the returned state - -#### Scenario: Provider status action is pending -- **WHEN** an authorized operator confirms enable, disable, or retire -- **THEN** only that provider action is disabled, duplicate submission is prevented, and success is shown only after a Platform response - -### Requirement: Session operation tray -The console SHALL expose a compact, theme-aware tray for current-session mutation records while clearly distinguishing it from durable Platform job and audit history. - -#### Scenario: Operation fails -- **WHEN** a tracked mutation fails -- **THEN** the tray shows the intent, safe target label, failure reason, and diagnostic identifier without exposing forbidden values - -#### Scenario: No session operations exist -- **WHEN** no mutation has been attempted in the current session -- **THEN** the tray remains compact and communicates that durable job and audit history is available on the relevant operational pages - -### Requirement: Theme, responsive, and secret safety -All new operations surfaces MUST use existing theme tokens and shared styles, MUST remain coherent in black-mecha and magical-girl themes at desktop and 390px, and MUST not render raw keys, tokens, secret values, host paths, PIDs, sockets, credentials, DSNs, RCON data, or direct Run endpoints. - -#### Scenario: Theme changes -- **WHEN** an operator switches between black-mecha and magical-girl themes -- **THEN** overview modules, lists, confirmations, progress rows, and the operation tray update atomically through existing theme variables without page-local global decoration - -#### Scenario: Narrow viewport renders operations surfaces -- **WHEN** the console is rendered at 390px width -- **THEN** controls and text remain within their containers, full-width working surfaces collapse deliberately, and no operational status or action is obscured - -#### Scenario: Narrow viewport navigation stays out of page flow -- **WHEN** the console is rendered at 390px width with navigation closed -- **THEN** the sidebar, account strip, and icon rail do not consume page height, and a small theme-aware left-edge menu control remains available - -#### Scenario: Operator opens and closes narrow navigation -- **WHEN** the operator activates the left-edge control or swipes right from within 28px of the viewport edge -- **THEN** an off-canvas vertical menu opens with icons and readable Chinese route labels in the existing order -- **AND** Escape, backdrop activation, route selection, or a leftward drawer swipe closes the menu - -#### Scenario: Rendered output is scanned for forbidden fragments -- **WHEN** first-party pages are rendered with API-backed fixtures during verification -- **THEN** forbidden raw secret, path, process, socket, credential, DSN, RCON, and direct Run endpoint fragments are absent diff --git a/openspec/changes/enrich-platform-operations-console/tasks.md b/openspec/changes/enrich-platform-operations-console/tasks.md deleted file mode 100644 index eb85790..0000000 --- a/openspec/changes/enrich-platform-operations-console/tasks.md +++ /dev/null @@ -1,69 +0,0 @@ -## 1. Shared Operations Contracts - -- [x] 1.1 Add shared frontend view contracts for module availability, job buckets, attention signals, and safe operation targets outside page components. -- [x] 1.2 Add pure derivation helpers for active/failed jobs, server attention sorting, and overview module summaries. -- [x] 1.3 Add focused tests for job classification, unavailable-module handling, deterministic ordering, and forbidden-field omission. - -## 2. Shared Operations UI - -- [x] 2.1 Add a compact theme-aware current-session operation tray with pending, succeeded, failed, and empty states. -- [x] 2.2 Pass the operation tracker through the shell and keep durable Platform job/audit history clearly distinguished from session feedback. -- [x] 2.3 Add Escape handling, duplicate-submit protection, and accessible labels to shared management and confirmation dialogs. - -## 3. Operations Overview - -- [x] 3.1 Refactor overview API modules so metrics, usage, providers, and audit events preserve independent loading, ready, empty, and unavailable states. -- [x] 3.2 Add actionable endpoint/job/resource/provider status modules and scoped retry/refresh timestamps using only safe Platform projections. -- [x] 3.3 Add overview tests covering partial API failure, active/failed jobs, actionable navigation, and read-only session behavior. - -## 4. Server Management - -- [x] 4.1 Add explicit metrics availability/retry and deterministic status/job sorting to the full-width server working surface. -- [x] 4.2 Add real active/failed job summaries and recovery navigation without reflowing cards or simulating terminal success. -- [x] 4.3 Add tests for metrics failure, job summaries, sorting, and permission-aware create/action controls. - -## 5. Plugin Marketplace - -- [x] 5.1 Add permission-gated confirmed install/enable/disable actions with per-action busy state and persisted-response refresh. -- [x] 5.2 Add scoped plugin detail retry that preserves the list summary when detail loading fails. -- [x] 5.3 Add tests for confirmation cancellation, duplicate-submit prevention, state failure recovery, and detail retry. - -## 6. User Management - -- [x] 6.1 Remove normal-page development user fallback substitution and render API failure with scoped retry. -- [x] 6.2 Add API-backed search, role/status filters, operational summaries, and clear-filter behavior to the full-width user list. -- [x] 6.3 Add tests for development API failure, filtering, mutation failure preservation, and platform-admin gating. - -## 7. AI Provider Management - -- [x] 7.1 Replace full-page reload retry with scoped provider loading and preserve explicit empty versus unavailable states. -- [x] 7.2 Add confirmed enable/disable/retire actions, provider-scoped busy state, persisted-response refresh, and safe key-presence copy. -- [x] 7.3 Add tests for scoped retry, confirmation cancellation, pending duplicate prevention, and failed action recovery. - -## 8. Theme and Responsive Integration - -- [x] 8.1 Add shared theme styles for operations modules, job rows, filters, confirmations, and the operation tray using existing tokens and frame materials. -- [x] 8.2 Add responsive rules for full-width working surfaces, dialogs, toolbars, and operation rows at 390px without page-local global decoration. -- [x] 8.3 Extend CSS contract tests for theme material reuse, radius limits, and nested-frame safety. -- [x] 8.4 Replace the narrow-screen top navigation stack with a theme-aware off-canvas text sidebar, left-edge effect handle, bounded swipe-open/close behavior, and accessible dismissal. -- [x] 8.5 Add focused component and CSS contract coverage for mobile labels, drawer state semantics, gesture thresholds, and no-flow layout. - -## 9. Verification - -- [x] 9.1 Run `cd platform_web && npm run typecheck`. -- [x] 9.2 Run `cd platform_web && npm test`. -- [x] 9.3 Run `cd platform_web && npm run build`. -- [x] 9.4 Run browser acceptance for all five first-party areas in black-mecha and magical-girl themes at desktop and 390px, including mobile drawer open/close, confirmation, and failure recovery. -- [x] 9.5 Scan rendered first-party pages for forbidden raw key, token, secret, path, PID, socket, credential, DSN, RCON, and direct Run endpoint fragments. -- [x] 9.6 Run `scripts/check-structure.sh`, `openspec validate enrich-platform-operations-console --strict`, and both repository `git diff --check` commands, then record evidence. - -## Verification Evidence - -- 2026-07-18 15:25 CST: `npm run typecheck`, `npm run build`, and the full Vitest suite passed (24 files, 132 tests). -- In-app browser acceptance passed against the real local Platform stack for 首页、服务器管理、插件市场、用户管理、AI 提供商管理 and server detail in `mecha-black` and `magical-girl` at 1440x960 and 390x844 (24 route/theme/viewport checks, zero horizontal overflow, zero console errors). -- The 390px sidebar stayed outside page flow (`mainTop=0`, `scrollWidth=390`), opened as a 300px icon-and-Chinese-text drawer, and closed by route selection; both themes were visually inspected. -- Confirmation cancellation preserved plugin state and dispatched no mutation. Real plugin persisted responses populated the session operation tray; a duplicate server ID produced a real Platform failure while preserving the form, and a corrected ID retry succeeded. A real dependency validation failure remained traceable in the staged task dialog. -- Rendered-page scans found no raw key/token/secret value, host path, PID, socket, credential, DSN, RCON value, or direct Run endpoint across all 24 route states. Safe ordinary labels such as key presence and non-projection notices were intentionally retained. -- `scripts/local-debug-smoke.sh` passed using Run-reported capabilities; absent `distribution.build` was verified as unavailable without fake success. `bash -n` and `node --check platform_web/acceptance/browser-acceptance.mjs` passed. -- `scripts/check-structure.sh`, `openspec validate enrich-platform-operations-console --strict`, main repository `git diff --check`, and independent Run repository `git diff --check` all passed. -- Structured browser evidence: `/private/tmp/browser-local-debug-acceptance-9k/browser-acceptance/item-9-evidence.json`. diff --git a/openspec/changes/establish-development-runtime-baseline/.openspec.yaml b/openspec/changes/establish-development-runtime-baseline/.openspec.yaml deleted file mode 100644 index 8e26fbe..0000000 --- a/openspec/changes/establish-development-runtime-baseline/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-02 diff --git a/openspec/changes/establish-development-runtime-baseline/design.md b/openspec/changes/establish-development-runtime-baseline/design.md deleted file mode 100644 index ff4d200..0000000 --- a/openspec/changes/establish-development-runtime-baseline/design.md +++ /dev/null @@ -1,75 +0,0 @@ -## Context - -The repository has the required project roots and architecture contracts, but there is no executable backend, run executor, frontend app, plugin SDK package, or common check command. Future changes need a stable local development baseline so every OpenSpec implementation can run tests and builds in the same way. - -The local environment currently has Go 1.25.1, Node 22.17.0, and npm 11.6.1. This change uses those tool families without adding business behavior beyond minimal health or placeholder shells. - -## Goals / Non-Goals - -**Goals:** - -- Add separate Go module baselines under `platform/` and `run/`. -- Add minimal executable entry points and tests for both Go roots. -- Add a Vite React TypeScript baseline under `platform_web/` with required route/page placeholders and a browser-verifiable shell. -- Add a TypeScript/npm baseline under `plugins/` for manifest schema validation, SDK exports, example fixtures, and tests. -- Add root orchestration scripts that run all baseline checks while keeping implementation code inside the owning roots. -- Document development commands and update structure checks for new required baseline files. - -**Non-Goals:** - -- No platform database implementation. -- No real platform API resource behavior beyond minimal health/bootstrap endpoints needed to prove the server starts. -- No run job execution, log ingest, artifact transfer, or game server lifecycle work. -- No plugin marketplace behavior or hosted plugin page runtime. -- No production deployment packaging. - -## Decisions - -### Decision 1: Use separate Go modules for `platform/` and `run/` - -`platform/` and `run/` SHALL each own a Go module, command entry point, internal packages, config loading, and tests. They must not import code from each other. Protocol sharing stays in documented contract files until a later OpenSpec introduces generated contracts. - -Alternative considered: one root Go module for both backend roots. Rejected because it would make casual cross-root imports too easy and weaken the ownership boundary required by `AGENTS.md`. - -### Decision 2: Use Vite, React, and TypeScript for `platform_web/` - -`platform_web/` SHALL use npm scripts for dev, build, typecheck, test, and preview. The baseline app should render the required first-party navigation entries and page placeholders without implementing backend-driven workflows. - -Alternative considered: a static HTML placeholder. Rejected because future frontend work needs route definitions, component structure, schema typing, and browser verification from the start. - -### Decision 3: Use npm TypeScript tooling for `plugins/` - -`plugins/` SHALL own its SDK package metadata, TypeScript sources, JSON schema validation scripts, example manifest fixtures, and tests. This keeps plugin contract checks close to plugin ownership while leaving platform registration behavior for a later change. - -Alternative considered: validate plugin schemas from `platform/`. Rejected because plugin authoring and fixture tests belong in the plugin workspace; platform can later consume the same published or copied contracts through an explicit boundary. - -### Decision 4: Root scripts orchestrate checks only - -Root `scripts/` may contain shell scripts such as `check-structure.sh` and `check-all.sh`, but no application logic. These scripts call commands inside each root and provide a single verification entry point for future OpenSpec changes. - -Alternative considered: a root package manager workspace. Deferred because there is not enough shared package structure yet, and root-level dependency metadata could blur ownership boundaries before generated contracts exist. - -### Decision 5: Minimal UI still requires browser verification - -Because this change creates the initial frontend shell, closure requires a local dev server and browser walkthrough. The walkthrough only needs to prove the shell renders, required navigation exists, and layout does not visibly overlap on desktop and mobile widths. - -Alternative considered: rely on build and unit tests only. Rejected because the repository rules require a browser walkthrough when frontend pages are touched. - -## Risks / Trade-offs - -- [Risk] Separate Go modules add repeated tooling setup. Mitigation: add root orchestration scripts and keep shared protocol files documented until generation is introduced. -- [Risk] Vite baseline may look like product UI before APIs exist. Mitigation: keep pages minimal and avoid fake workflows; later changes will implement data-backed pages. -- [Risk] npm dependency versions may drift. Mitigation: commit lockfiles during implementation and document the Node/npm baseline. -- [Risk] `scripts/check-all.sh` may be slow as features grow. Mitigation: start with baseline commands and allow future changes to add narrower scripts when needed. - -## Migration Plan - -1. Add module/package metadata and minimal source files inside each project root. -2. Add root orchestration scripts and update `scripts/check-structure.sh` for new required baseline files. -3. Update README files with local development commands. -4. Run root structure checks, per-root tests/builds, strict OpenSpec validation, and frontend browser walkthrough. - -## Open Questions - -- Whether future generated contracts should be produced from OpenAPI, protobuf, JSON Schema, or TypeScript source remains for a later contract-generation change. -- Whether `platform/` starts with SQLite or Postgres remains for the platform API surface change. diff --git a/openspec/changes/establish-development-runtime-baseline/proposal.md b/openspec/changes/establish-development-runtime-baseline/proposal.md deleted file mode 100644 index d6a5f37..0000000 --- a/openspec/changes/establish-development-runtime-baseline/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -## Why - -The repository currently has architecture documents and ownership directories, but it does not yet have runnable project baselines. Later platform, run, frontend, and plugin changes need consistent local commands, package boundaries, and verification entry points before business behavior is implemented. - -## What Changes - -- Establish Go module baselines for `platform/` and `run/` with minimal executable entry points and tests. -- Establish a TypeScript/Vite baseline for `platform_web/` with a minimal browser-rendered management console shell. -- Establish a TypeScript baseline for `plugins/` covering SDK exports, schema validation scripts, examples, and contract tests. -- Add root orchestration scripts for build/test/check workflows without placing implementation code outside the matching project roots. -- Document local development commands and required tool versions. -- Extend structure verification only for new required baseline files and directories introduced by this change. - -## Capabilities - -### New Capabilities -- `development-runtime-baseline`: Runtime, tooling, command, and verification baseline for the four project roots. - -### Modified Capabilities -- None. - -## Impact - -- Affects `platform/`, `run/`, `platform_web/`, `plugins/`, root documentation, and root verification scripts. -- Introduces Go and npm-based development commands but does not implement platform business APIs, run job execution, plugin marketplace behavior, or full frontend pages. -- Future OpenSpec changes will rely on these commands for tests, builds, and local walkthroughs. diff --git a/openspec/changes/establish-development-runtime-baseline/specs/development-runtime-baseline/spec.md b/openspec/changes/establish-development-runtime-baseline/specs/development-runtime-baseline/spec.md deleted file mode 100644 index 1632f66..0000000 --- a/openspec/changes/establish-development-runtime-baseline/specs/development-runtime-baseline/spec.md +++ /dev/null @@ -1,67 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform Go Runtime Baseline -`platform/` SHALL contain an independent Go module with a minimal command entry point, configuration package, HTTP health surface, and automated tests. - -#### Scenario: Platform tests run -- **WHEN** a contributor runs the documented platform test command -- **THEN** the platform Go module test suite completes successfully without importing code from `run/`, `platform_web/`, or `plugins/` - -#### Scenario: Platform server starts -- **WHEN** a contributor runs the documented platform development command -- **THEN** the process starts a local HTTP server with a health response suitable for smoke testing - -### Requirement: Run Go Runtime Baseline -`run/` SHALL contain an independent Go module with a minimal command entry point, configuration package, platform client boundary, and automated tests. - -#### Scenario: Run tests run -- **WHEN** a contributor runs the documented run test command -- **THEN** the run Go module test suite completes successfully without importing code from `platform/`, `platform_web/`, or `plugins/` - -#### Scenario: Run executor starts -- **WHEN** a contributor runs the documented run development command -- **THEN** the process starts in a local smoke-test mode without exposing host paths, raw credentials, or direct sockets to plugins or frontend code - -### Requirement: Platform Web TypeScript Runtime Baseline -`platform_web/` SHALL contain a Vite React TypeScript app with route definitions, required first-party page placeholders, API client boundaries, shared components, theme tokens, and automated build/typecheck/test scripts. - -#### Scenario: Frontend checks run -- **WHEN** a contributor runs the documented platform_web verification commands -- **THEN** TypeScript typecheck, tests, and production build complete successfully - -#### Scenario: Required navigation renders -- **WHEN** the platform_web dev server is opened in a browser -- **THEN** the shell renders navigation entries for 首页、服务器管理、插件市场、用户管理、AI 提供商管理 without visible overlap on desktop and mobile widths - -### Requirement: Plugin Workspace TypeScript Baseline -`plugins/` SHALL contain npm TypeScript tooling for SDK exports, JSON schema validation, example plugin fixtures, and automated tests. - -#### Scenario: Plugin checks run -- **WHEN** a contributor runs the documented plugin verification commands -- **THEN** SDK typecheck, schema validation, and tests complete successfully - -#### Scenario: Example manifest validates -- **WHEN** the plugin schema validation command is run -- **THEN** `plugins/examples/dev-game-plugin/manifest.json` validates against `plugins/manifests/game-plugin.manifest.schema.json` - -### Requirement: Root Verification Orchestration -The repository SHALL provide root-level verification scripts that orchestrate structure checks and per-root build/test commands without containing application logic. - -#### Scenario: Full baseline check runs -- **WHEN** a contributor runs the documented full check command from the repository root -- **THEN** it runs structure verification plus platform, run, platform_web, and plugin baseline checks - -#### Scenario: Structure rules include new baseline files -- **WHEN** `scripts/check-structure.sh` runs after this change -- **THEN** it verifies the new required module, package, command, test, and documentation baseline files added by this change - -### Requirement: Development Documentation -The repository SHALL document required tool versions, local development commands, verification commands, and the scope limits of this runtime baseline. - -#### Scenario: Contributor reads the README -- **WHEN** a contributor reads the root and per-root README files -- **THEN** they can identify how to install dependencies, start local processes, run tests, run builds, and perform the frontend browser walkthrough - -#### Scenario: Future change reads baseline docs -- **WHEN** a future OpenSpec implementation needs to add product behavior -- **THEN** it can reuse the documented baseline commands instead of inventing a new verification surface diff --git a/openspec/changes/establish-development-runtime-baseline/tasks.md b/openspec/changes/establish-development-runtime-baseline/tasks.md deleted file mode 100644 index 1135323..0000000 --- a/openspec/changes/establish-development-runtime-baseline/tasks.md +++ /dev/null @@ -1,75 +0,0 @@ -## 1. Go Runtime Baselines - -- [x] 1.1 Add an independent Go module under `platform/` with minimal command, config, HTTP health surface, and tests. -- [x] 1.2 Add an independent Go module under `run/` with minimal command, config, platform client boundary, smoke-test mode, and tests. -- [x] 1.3 Verify neither Go module imports implementation code from another project root. - -## 2. Platform Web Baseline - -- [x] 2.1 Add npm, Vite, React, and TypeScript baseline files under `platform_web/`. -- [x] 2.2 Add route definitions, API client boundaries, required page placeholders, shared components, theme tokens, and test setup in the required directories. -- [x] 2.3 Add scripts for `dev`, `build`, `typecheck`, `test`, and `preview`. - -## 3. Plugin Workspace Baseline - -- [x] 3.1 Add npm and TypeScript baseline files under `plugins/`. -- [x] 3.2 Add SDK export stubs, schema validation scripts, example manifest validation, and tests inside the plugin root. -- [x] 3.3 Keep plugin checks scoped to plugin contracts and do not add platform marketplace behavior in this change. - -## 4. Root Orchestration And Documentation - -- [x] 4.1 Add a root verification script that runs structure checks plus each root's baseline checks without containing application logic. -- [x] 4.2 Update `scripts/check-structure.sh` for new required baseline files and directories. -- [x] 4.3 Update root and per-root README files with tool versions, dependency install commands, local start commands, verification commands, and baseline scope limits. - -## 5. Verification - -- [x] 5.1 Run `go test ./...` in `platform/`. -- [x] 5.2 Run `go test ./...` in `run/`. -- [x] 5.3 Run the documented install, typecheck, test, and build commands in `platform_web/`. -- [x] 5.4 Run the documented install, typecheck, test, and schema validation commands in `plugins/`. -- [x] 5.5 Run the root full-check script and `scripts/check-structure.sh`. -- [x] 5.6 Start the platform_web dev server and complete a browser walkthrough at desktop and mobile widths. -- [x] 5.7 Run `openspec validate establish-development-runtime-baseline --strict`. - -## Evidence - -- `go test ./...` in `platform/`: passed. -- `go test ./...` in `run/`: passed. -- `npm install`, `npm run typecheck`, `npm run test`, and `npm run build` in `platform_web/`: passed. -- `npm install`, `npm run typecheck`, `npm run test`, and `npm run validate:manifest` in `plugins/`: passed. -- Cross-root import checks: `rg "browser\.local/(run|platform_web|plugins)" platform` and `rg "browser\.local/(platform|platform_web|plugins)" run` returned no matches. -- `scripts/check-all.sh`: passed. -- `scripts/check-structure.sh`: passed. -- Browser walkthrough: Vite dev server at `http://127.0.0.1:5173/`; desktop 1440x900 and mobile 390x844 checks confirmed required labels, 5 navigation items, 3 metric cards, no nav/metric/header overlap, and mobile document width equal to viewport. -- `openspec validate establish-development-runtime-baseline --strict`: passed. - -## Implementation Handoff - -```text -Implement OpenSpec change: establish-development-runtime-baseline - -Scope: -- Implement only openspec/changes/establish-development-runtime-baseline/. -- Add runtime/tooling baselines for platform, run, platform_web, and plugins. -- Do not implement platform business APIs, run job execution, log ingest, artifact transfer, plugin marketplace workflows, billing, cloud host sales, or agent-provider/cloud-provider workflows. - -Read first: -- AGENTS.md -- platform/AGENTS.md -- run/AGENTS.md -- platform_web/AGENTS.md -- plugins/AGENTS.md -- openspec/changes/bootstrap-game-server-platform-architecture/proposal.md -- openspec/changes/bootstrap-game-server-platform-architecture/design.md -- openspec/changes/architecture-delivery-stream/delivery-plan.md -- openspec/changes/establish-development-runtime-baseline/proposal.md -- openspec/changes/establish-development-runtime-baseline/design.md -- openspec/changes/establish-development-runtime-baseline/tasks.md - -Required closure: -- Mark task checkboxes complete only after evidence exists. -- Run platform Go tests, run Go tests, platform_web install/typecheck/test/build, plugin install/typecheck/test/schema validation, the root full-check script, scripts/check-structure.sh, and openspec validate establish-development-runtime-baseline --strict. -- Because platform_web pages are touched, start the dev server and perform a browser walkthrough at desktop and mobile widths before claiming the UI is accepted. -- Stop after this change is closed; do not start implement-platform-core-domain in the same chat unless explicitly asked. -``` diff --git a/openspec/changes/fix-custom-background-status/proposal.md b/openspec/changes/fix-custom-background-status/proposal.md deleted file mode 100644 index c1f2705..0000000 --- a/openspec/changes/fix-custom-background-status/proposal.md +++ /dev/null @@ -1,17 +0,0 @@ -## Why - -Profile settings correctly render an uploaded custom background, but the page header metric and background preset controls still label the built-in preset as the current background. This makes users think the uploaded background was ignored when the built-in preset is actually only the fallback after upload removal. - -## What Changes - -- Show an uploaded custom background as the active background in profile settings summary metrics. -- Treat the selected built-in background preset as a fallback while a custom background is active. -- Label the fallback preset explicitly and avoid marking it as the pressed/active visible background. -- Keep uploaded background precedence and existing theme visual direction unchanged. - -## Impact - -- Affected root: `platform_web/`. -- Expected files: `platform_web/pages/ProfileSettingsPage.tsx`, `platform_web/theme/base.css`, and focused page rendering tests. -- No platform API, persistence schema, authentication, plugin, run, or server management behavior changes. -- Verification: focused frontend tests, `scripts/check-structure.sh`, and `openspec validate fix-custom-background-status --strict`. diff --git a/openspec/changes/fix-custom-background-status/specs/platform-web-custom-background-status/spec.md b/openspec/changes/fix-custom-background-status/specs/platform-web-custom-background-status/spec.md deleted file mode 100644 index ad7e384..0000000 --- a/openspec/changes/fix-custom-background-status/specs/platform-web-custom-background-status/spec.md +++ /dev/null @@ -1,19 +0,0 @@ -## ADDED Requirements - -### Requirement: Profile settings distinguish active custom backgrounds from fallback presets - -The platform_web profile settings page SHALL report an uploaded custom background as the active workspace background while preserving the selected built-in preset only as the fallback after upload removal. - -#### Scenario: Uploaded background is active - -- **WHEN** a user has configured an uploaded custom background -- **AND** a built-in background preset is also selected -- **THEN** the profile settings summary MUST label the active background as custom rather than naming the built-in preset as current -- **AND** the selected built-in preset MUST be visibly identified as a fallback that appears after the uploaded background is removed -- **AND** the uploaded background MUST remain the workspace desktop - -#### Scenario: Uploaded background is removed - -- **WHEN** a user removes the uploaded custom background -- **THEN** the previously selected built-in preset becomes the visible active background again -- **AND** the profile settings summary MAY name that built-in preset as current diff --git a/openspec/changes/fix-custom-background-status/tasks.md b/openspec/changes/fix-custom-background-status/tasks.md deleted file mode 100644 index 1cbc861..0000000 --- a/openspec/changes/fix-custom-background-status/tasks.md +++ /dev/null @@ -1,22 +0,0 @@ -## 1. Custom Background Status - -- [x] 1.1 Show uploaded custom backgrounds as the active background in the profile settings header metric. -- [x] 1.2 Render the selected built-in preset as a clearly labeled fallback while an uploaded background is active. -- [x] 1.3 Preserve fallback preset selection so removing the uploaded background restores the selected built-in desktop. - -## 2. Verification - -- [x] 2.1 Add focused rendering coverage for uploaded-background labeling. -- [x] 2.2 Run focused frontend tests. -- [x] 2.3 Run `scripts/check-structure.sh`. -- [x] 2.4 Run `openspec validate fix-custom-background-status --strict`. - -## Evidence - -- `npm test -- pages/ConsolePages.test.tsx` passed 10 tests. -- `npm test` passed 13 files / 68 tests. -- `npm run typecheck` passed. -- `npm run build` passed. -- `scripts/check-structure.sh` passed. -- `openspec validate fix-custom-background-status --strict` passed; the command printed a PostHog network flush warning after validation because the sandbox cannot resolve `edge.openspec.dev`. -- Runtime UI check on `http://127.0.0.1:5173/#/profile` confirmed `data-custom-background="true"`, summary `背景 自定义背景`, fallback chip `机甲格纳库 备用`, and theme switch black mecha -> magical-girl preserved the uploaded background. diff --git a/openspec/changes/fix-env-profile-settings/.openspec.yaml b/openspec/changes/fix-env-profile-settings/.openspec.yaml deleted file mode 100644 index aee4ef1..0000000 --- a/openspec/changes/fix-env-profile-settings/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-07 diff --git a/openspec/changes/fix-env-profile-settings/design.md b/openspec/changes/fix-env-profile-settings/design.md deleted file mode 100644 index bd06bee..0000000 --- a/openspec/changes/fix-env-profile-settings/design.md +++ /dev/null @@ -1,51 +0,0 @@ -## Context - -The platform already supports file and MySQL-backed metadata stores behind `repo.Store`. MySQL initialization is selected by `PLATFORM_STORAGE_BACKEND=mysql` and uses `PLATFORM_MYSQL_DSN`, but `platform/cmd/platform` calls `config.Load()` directly and `config.Load()` only reads process environment variables. A developer who edits `platform/.env` and starts the binary without sourcing that file still gets the default file-backed store. - -The platform web shell currently embeds profile editing, theme palette selection, background presets, upload background, and logout inside the sidebar account popover. Those controls already call the current-user profile and theme APIs, but the interaction is cramped and visually hard to use. - -## Goals / Non-Goals - -**Goals:** -- Load repository-local `.env` values into platform configuration before storage initialization. -- Preserve process environment precedence over `.env` values so deployment systems can override local files. -- Keep profile and theme persistence backed by existing user APIs and the configured metadata repository. -- Replace the account popover editor with a dedicated personal settings page that is available to all authenticated users. -- Preserve the current magical crystal-moonlight shell style and shared surface classes. - -**Non-Goals:** -- Add a normalized relational user schema or migrations beyond the existing MySQL metadata snapshot table. -- Add external account providers, billing, cloud host provisioning, or plugin marketplace workflows. -- Expose raw credentials, host paths, or direct run/plugin internals to the web UI. - -## Decisions - -1. **Load `.env` inside `platform/config`** - - `config.Load()` will call a small local dotenv loader before reading values. - - The loader will check common local paths such as `.env` and `platform/.env` relative to the current working directory. - - Existing process environment values win over file values. - - Alternative considered: requiring users to `source .env`. Rejected because the observed failure is that local `.env` exists but platform startup does not consume it. - -2. **Keep MySQL persistence through the existing snapshot repository** - - The fix only makes backend selection reliable; it does not introduce normalized SQL tables for users. - - Profile and theme updates already flow through `UpdateUser`, which persists through `repo.Store`; this remains the single write path. - - Alternative considered: adding user-specific SQL tables now. Rejected because it is broader than the current bug and would duplicate the existing store abstraction. - -3. **Move personal settings to a route instead of a popover** - - Add a `profileSettings` page id, route, registry entry, and page component. - - The sidebar account control becomes a navigation entry point to that page, with logout remaining available from the settings page. - - Theme controls move into the page but continue to use `theme/tokens.ts` helpers and the session store API methods. - - Alternative considered: converting the popover into a larger drawer. Rejected because the user specifically wants a normal personal configuration interface, and a page is more ergonomic for forms and preview grids. - -## Risks / Trade-offs - -- [Risk] `.env` parsing can accidentally override production environment values. → Mitigation: only set variables that are not already present in `os.Environ`. -- [Risk] Multiple working directories make `.env` discovery ambiguous. → Mitigation: try root `.env` and `platform/.env` from the process working directory, and use deterministic later-file fallback only for missing keys. -- [Risk] Uploaded background data URLs can be large. → Mitigation: preserve the existing client-side behavior and persistence API contract rather than expanding backend payload rules in this change. -- [Risk] Removing the popover editor changes a familiar access point. → Mitigation: keep the sidebar account button visible and route it directly to the new personal settings page. - -## Migration Plan - -1. Add dotenv loading tests that prove `platform/.env` selects MySQL settings and explicit process env overrides file values. -2. Add the personal settings route/page and update existing shell/session wiring to keep API-backed persistence. -3. Validate OpenSpec, backend config tests, frontend tests/typecheck/build, structure checks, and a browser walkthrough for the personal settings page. diff --git a/openspec/changes/fix-env-profile-settings/proposal.md b/openspec/changes/fix-env-profile-settings/proposal.md deleted file mode 100644 index 228de70..0000000 --- a/openspec/changes/fix-env-profile-settings/proposal.md +++ /dev/null @@ -1,30 +0,0 @@ -## Why - -Local operators can configure `platform/.env`, but the platform process currently reads only inherited environment variables. This makes MySQL metadata storage look uninitialized even when the `.env` file contains the correct `PLATFORM_STORAGE_BACKEND=mysql` and `PLATFORM_MYSQL_DSN` settings. - -The current personal configuration experience is embedded in the sidebar account popover, which is cramped for profile, theme, and background preferences. Operators need a normal first-party personal settings page that saves profile and theme changes through platform APIs so values are durable in the configured metadata store. - -## What Changes - -- Load platform environment variables from local `.env` files before building platform configuration, while preserving explicit process environment overrides. -- Keep MySQL metadata initialization database-backed and make configured storage selection testable so operators can verify the remote metadata store is actually used. -- Replace the sidebar profile popover with a dedicated personal settings page for profile, theme palette, background preset, uploaded background, and logout actions. -- Add the personal settings route to the shell for authenticated users and keep account edits wired to `/api/v1/users/current/profile` and `/api/v1/users/current/theme`. -- Preserve the magical-girl crystal-moonlight visual system by reusing shared shell/page surfaces and theme tokens rather than adding a one-off UI style. - -## Capabilities - -### New Capabilities - -- `platform-env-bootstrap`: Platform startup loads local environment configuration and initializes the configured metadata backend predictably. -- `personal-settings-workspace`: Authenticated users manage their own profile and console theme preferences from a full settings page backed by platform APIs. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/config` and platform startup tests for `.env` loading and storage backend selection. -- Affects `platform_web` route definitions, page registry, shell account controls, session usage, API-bound personal settings UI, tests, and shared styling. -- Does not add billing, cloud host sales, external marketplace behavior, raw AI key exposure, or plugin direct access to platform/run internals. diff --git a/openspec/changes/fix-env-profile-settings/specs/personal-settings-workspace/spec.md b/openspec/changes/fix-env-profile-settings/specs/personal-settings-workspace/spec.md deleted file mode 100644 index f91d4af..0000000 --- a/openspec/changes/fix-env-profile-settings/specs/personal-settings-workspace/spec.md +++ /dev/null @@ -1,34 +0,0 @@ -## ADDED Requirements - -### Requirement: Authenticated users have a personal settings page -The platform web application SHALL provide a normal page-level personal settings workspace for every authenticated user. - -#### Scenario: User opens personal settings -- **WHEN** an authenticated user activates the account settings entry point -- **THEN** the application MUST navigate to a full personal settings page instead of opening a cramped profile popover - -#### Scenario: User sees current account data -- **WHEN** the personal settings page renders -- **THEN** it MUST show the current user's display name, email, role labels, status, profile fields, theme palette, background preset, and custom background state - -### Requirement: Personal profile saves through platform APIs -The personal settings page SHALL save editable profile fields through platform-mediated current-user APIs. - -#### Scenario: User saves profile -- **WHEN** a user submits display name, avatar URL, phone, QQ, or contact note changes -- **THEN** the page MUST call the current-user profile API and render the updated current user from the response - -#### Scenario: Profile API is unavailable -- **WHEN** the current-user profile API cannot be reached in local development fallback mode -- **THEN** the page MUST mark the resulting profile state as local fallback rather than pretending database persistence succeeded - -### Requirement: Theme preferences save through platform APIs -The personal settings page SHALL save theme palette, background preset, and custom background preferences through platform-mediated current-user APIs where available. - -#### Scenario: User changes theme preference -- **WHEN** a user selects a palette, selects a background preset, uploads a background, or removes a background -- **THEN** the page MUST update the visible theme and persist the preference through the current-user theme API - -#### Scenario: Theme API is unavailable -- **WHEN** the current-user theme API cannot be reached in local development fallback mode -- **THEN** the page MUST preserve local theme preference behavior and clearly show that the preference is local diff --git a/openspec/changes/fix-env-profile-settings/specs/platform-env-bootstrap/spec.md b/openspec/changes/fix-env-profile-settings/specs/platform-env-bootstrap/spec.md deleted file mode 100644 index 6b1e2f2..0000000 --- a/openspec/changes/fix-env-profile-settings/specs/platform-env-bootstrap/spec.md +++ /dev/null @@ -1,23 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform startup loads local environment files -The platform SHALL load local `.env` configuration before constructing runtime configuration for storage initialization. - -#### Scenario: Platform env file selects MySQL storage -- **WHEN** `platform/.env` contains `PLATFORM_STORAGE_BACKEND=mysql` and `PLATFORM_MYSQL_DSN` -- **THEN** platform configuration MUST use the MySQL storage backend and DSN from the env file - -#### Scenario: Process environment overrides env file -- **WHEN** a process environment variable and a local `.env` file both define the same platform setting -- **THEN** platform configuration MUST use the process environment value - -### Requirement: Metadata backend selection remains database-backed -The platform SHALL initialize the configured metadata repository through the existing store boundary rather than falling back to hardcoded local sample data. - -#### Scenario: MySQL storage is configured -- **WHEN** `PLATFORM_STORAGE_BACKEND=mysql` and a non-empty `PLATFORM_MYSQL_DSN` are loaded -- **THEN** platform startup MUST initialize the MySQL metadata store - -#### Scenario: MySQL storage is missing DSN -- **WHEN** `PLATFORM_STORAGE_BACKEND=mysql` is loaded without `PLATFORM_MYSQL_DSN` -- **THEN** platform startup MUST fail with a direct configuration error instead of silently using file or memory storage diff --git a/openspec/changes/fix-env-profile-settings/tasks.md b/openspec/changes/fix-env-profile-settings/tasks.md deleted file mode 100644 index 08ff86e..0000000 --- a/openspec/changes/fix-env-profile-settings/tasks.md +++ /dev/null @@ -1,31 +0,0 @@ -## 1. Platform Env Bootstrap - -- [x] 1.1 Add a small dotenv loader in `platform/config` that reads local `.env` files without overriding explicit process environment values. -- [x] 1.2 Add config tests for `platform/.env` MySQL settings, process env precedence, and missing DSN behavior through storage initialization. - -## 2. Personal Settings Workspace - -- [x] 2.1 Add a first-party personal settings route, page id, registry entry, and navigation entry point for authenticated users. -- [x] 2.2 Move profile, theme palette, background preset, uploaded background, and logout controls from the sidebar popover into the new page. -- [x] 2.3 Keep profile and theme saves wired to current-user APIs and show API vs local fallback persistence state. -- [x] 2.4 Add/update frontend tests for routing, shell account navigation, and profile/theme API calls. - -## 3. Verification - -- [x] 3.1 Run `cd platform && go test ./config ./api ./service ./repo -count=1`. -- [x] 3.2 Run `cd platform_web && npm run typecheck && npm test && npm run build`. -- [x] 3.3 Run `scripts/check-structure.sh`. -- [x] 3.4 Run `openspec validate fix-env-profile-settings --strict`. -- [x] 3.5 Perform a browser walkthrough for the personal settings page and record the result. - -## Verification Evidence - -- 2026-07-07: `cd platform && go test ./config ./api ./service ./repo -count=1` passed. -- 2026-07-07: `cd platform_web && npm run typecheck` passed. -- 2026-07-07: `cd platform_web && npm test` passed with 11 files / 47 tests. -- 2026-07-07: `cd platform_web && npm run build` passed and Vite produced `dist/` assets. -- 2026-07-07: `scripts/check-structure.sh` passed. -- 2026-07-07: `openspec validate fix-env-profile-settings --strict` reported the change is valid; PostHog telemetry flush failed due restricted DNS and did not affect validation. -- 2026-07-07: Started `cd platform_web && npm run dev -- --port 5173`; Vite served the app at `http://127.0.0.1:5174/` because 5173 was occupied. Browser walkthrough could not be completed in this tool session because no in-app browser/Chrome control tool or local Playwright/Puppeteer dependency was exposed. -- 2026-07-08: Rechecked task `3.5` before generating the next architecture-stream OpenSpec. The browser walkthrough remains explicitly blocked in this session: an in-app browser connection opened the auth page at `http://127.0.0.1:5177/`, but DOM snapshot capture failed with `TypeError: o.incrementalAriaSnapshot is not a function`; the fallback-enabled dev server then failed to bind requested localhost ports with `listen EPERM` for `127.0.0.1:5180`, `127.0.0.1:5173`, and `127.0.0.1:5177`. The walkthrough is not accepted; it must be rerun manually or in a working browser/dev-server session before closing this change. -- 2026-07-08: Browser walkthrough accepted after starting the platform API with file storage at `127.0.0.1:18080` and using the existing Vite dev server at `127.0.0.1:5173`. Logged in with the seeded API-backed platform administrator `operator.local@example.test`, landed on `#/home`, opened `#/profile`, verified `个人设置` showed `API 已连接`, `Operator`, `operator.local@example.test`, `active`, and platform-admin navigation. Edited the contact note to `api walkthrough verified 2026-07-08`, clicked `保存资料`, observed `个人资料已保存到数据库`, reloaded `#/profile`, and confirmed the note value persisted through the API-backed session. diff --git a/openspec/changes/fix-platform-auth-session-api/.openspec.yaml b/openspec/changes/fix-platform-auth-session-api/.openspec.yaml deleted file mode 100644 index d86f152..0000000 --- a/openspec/changes/fix-platform-auth-session-api/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-04 diff --git a/openspec/changes/fix-platform-auth-session-api/design.md b/openspec/changes/fix-platform-auth-session-api/design.md deleted file mode 100644 index 3c996a9..0000000 --- a/openspec/changes/fix-platform-auth-session-api/design.md +++ /dev/null @@ -1,16 +0,0 @@ -## Design - -- Sessions are in-memory platform sessions keyed by a random bearer token. Clients send the token as `Authorization: Bearer `. -- The local development platform seeds one explicit platform administrator account so real login can reach the admin console: - - account/email: `operator.local@example.test` - - password: `operator-local` -- Passwords are stored as PBKDF2-SHA256 hashes with per-user salts using only Go standard library primitives. -- Public registration creates a pending user with `server-admin` role and returns `status=pending` rather than authenticating the user. -- Current-user profile and theme updates operate only on the authenticated session user and return bounded DTOs. -- User management updates reuse `PUT /api/v1/users/{id}` and allow status, roles, display name, email, and profile fields to be changed through service validation. - -## Security Notes - -- Password hashes are not returned in DTOs. -- Pending/disabled users cannot log in. -- The frontend local fallback is disabled unless `VITE_ENABLE_LOCAL_AUTH_FALLBACK=true`, and its fallback user is not a platform admin. diff --git a/openspec/changes/fix-platform-auth-session-api/proposal.md b/openspec/changes/fix-platform-auth-session-api/proposal.md deleted file mode 100644 index 08f1315..0000000 --- a/openspec/changes/fix-platform-auth-session-api/proposal.md +++ /dev/null @@ -1,17 +0,0 @@ -## Why - -The platform_web console already calls authentication, current-user, profile, theme, and user update endpoints, but the platform API has those routes deferred. That mismatch makes login/register appear broken and encourages the frontend local fallback to grant a platform administrator session without credentials. - -## What Changes - -- Add a minimal first-party username/email + password session API for login, registration, logout, and current-user lookup. -- Store password hashes in platform-owned user records and never expose password material to platform_web. -- Default public registration to pending server-admin scope instead of platform administrator privileges. -- Add controlled user update support so the 用户管理 page can change user status through the API. -- Restrict frontend local fallback to development/demo mode and downgrade it away from platform administrator privileges. - -## Impact - -- Affects `platform/` and `platform_web/`. -- Keeps authentication in platform only; plugins do not receive raw credentials or auth secrets. -- Does not add OAuth, SMS, production persistence, billing, cloud host sales, or unrelated marketplace behavior. diff --git a/openspec/changes/fix-platform-auth-session-api/specs/platform-auth-session/spec.md b/openspec/changes/fix-platform-auth-session-api/specs/platform-auth-session/spec.md deleted file mode 100644 index c20245c..0000000 --- a/openspec/changes/fix-platform-auth-session-api/specs/platform-auth-session/spec.md +++ /dev/null @@ -1,56 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform authentication sessions are implemented -The platform SHALL expose login, registration, logout, and current-user routes backed by platform-owned user records and in-memory session tokens. - -#### Scenario: Active user logs in -- **WHEN** a client posts a valid account and password to `POST /api/v1/auth/login` -- **THEN** the platform MUST return `200` with an authenticated `AuthSessionResponse` and a session token - -#### Scenario: Pending user cannot log in -- **WHEN** a pending user submits valid credentials -- **THEN** the platform MUST reject the login with `403` and MUST NOT issue a session token - -#### Scenario: Current user is requested -- **WHEN** a client sends `GET /api/v1/users/current` with a valid bearer session token -- **THEN** the platform MUST return the bounded current user DTO without password material - -#### Scenario: Session logs out -- **WHEN** a client posts to `POST /api/v1/auth/logout` with a valid bearer token -- **THEN** the platform MUST invalidate that session token - -### Requirement: Registration is low privilege by default -Public registration SHALL create pending users with server scope and SHALL NOT grant platform administrator privileges. - -#### Scenario: Visitor registers -- **WHEN** a visitor submits display name, email, and password to `POST /api/v1/auth/register` -- **THEN** the platform MUST create a pending user with a non-platform-admin role and return `status=pending` - -### Requirement: User management updates are supported -The platform SHALL support controlled user updates through `PUT /api/v1/users/{id}` using named DTOs and service validation. - -#### Scenario: User status is updated -- **WHEN** a platform client sends a valid status update for an existing user -- **THEN** the platform MUST persist and return the updated user DTO - -### Requirement: Current user preferences are supported -The platform SHALL allow an authenticated current user to update bounded profile and theme preference fields. - -#### Scenario: Current user profile is updated -- **WHEN** a client sends `PUT /api/v1/users/current/profile` with a valid bearer session token -- **THEN** the platform MUST persist the bounded profile fields and return the updated current user DTO - -#### Scenario: Current user theme is updated -- **WHEN** a client sends `PUT /api/v1/users/current/theme` with a valid bearer session token -- **THEN** the platform MUST persist the theme preference and return a `UserThemePreferenceResponse` - -### Requirement: Frontend fallback cannot silently grant platform admin -The frontend SHALL NOT persist a local platform administrator user as a fallback authentication path. - -#### Scenario: Auth API is unavailable -- **WHEN** the auth API is unavailable and local fallback is not explicitly enabled -- **THEN** the frontend MUST keep the user on the authentication screen and MUST NOT enter the console as platform administrator - -#### Scenario: Development fallback is enabled -- **WHEN** local fallback is explicitly enabled -- **THEN** the fallback user MUST have server-scoped access only and MUST NOT expose platform administrator navigation diff --git a/openspec/changes/fix-platform-auth-session-api/tasks.md b/openspec/changes/fix-platform-auth-session-api/tasks.md deleted file mode 100644 index 06b1cde..0000000 --- a/openspec/changes/fix-platform-auth-session-api/tasks.md +++ /dev/null @@ -1,21 +0,0 @@ -## 1. OpenSpec And Contracts - -- [x] 1.1 Add auth/session requirements covering login, registration, logout, current-user, profile/theme updates, user update, and local fallback limits. - -## 2. Platform Implementation - -- [x] 2.1 Extend user domain, DTO, model, validation, and service contracts for password hashes, profile, theme, and controlled user updates. -- [x] 2.2 Implement in-memory platform auth sessions and route handlers for `/api/v1/auth/*` and `/api/v1/users/current*`. -- [x] 2.3 Implement `PUT /api/v1/users/{id}` for the 用户管理 page. - -## 3. Frontend Implementation - -- [x] 3.1 Send bearer session tokens on API calls and persist only the API session token, not a privileged local user. -- [x] 3.2 Gate local fallback behind an explicit dev/demo env flag and ensure fallback never grants platform administrator privileges. -- [x] 3.3 Keep metrics/config/AI suggestion gaps in graceful page-local fallback behavior. - -## 4. Verification - -- [x] 4.1 Add backend API/service tests for login/register/current-user/logout/pending/disabled/user-update behavior. -- [x] 4.2 Add frontend session tests for API login, failed auth, refresh session restoration, and fallback gating. -- [x] 4.3 Run platform tests, platform_web tests/typecheck/build, `scripts/check-structure.sh`, and `openspec validate fix-platform-auth-session-api --strict`. diff --git a/openspec/changes/fix-run-build-worker-role/.openspec.yaml b/openspec/changes/fix-run-build-worker-role/.openspec.yaml deleted file mode 100644 index 8e7013b..0000000 --- a/openspec/changes/fix-run-build-worker-role/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-27 diff --git a/openspec/changes/fix-run-build-worker-role/design.md b/openspec/changes/fix-run-build-worker-role/design.md deleted file mode 100644 index ef71e68..0000000 --- a/openspec/changes/fix-run-build-worker-role/design.md +++ /dev/null @@ -1,42 +0,0 @@ -## Context - -Platform dispatches `distribution.build` through a trusted Run endpoint. A newer server lifecycle already distinguishes `DeploymentTargetID` from a dedicated `RunEndpointID`, but legacy instances can still have only `RunEndpointID`. A generated Run for such an instance was built with the shared endpoint identity and its component key, so starting it remotely replaced the local build worker registration. - -## Goals / Non-Goals - -**Goals:** - -- Preserve a local trusted Run worker as the only component that compiles generated Windows Run binaries. -- Give generated Run packages a deterministic server-scoped identity. -- Make legacy generation self-heal the unsafe endpoint binding before a build is queued. -- Prevent generated Run packages from advertising build-only worker capabilities. - -**Non-Goals:** - -- Do not move compilation into the browser, expose source trees to plugins, or require Go/Git on the game host. -- Do not alter deployment commands, game dependencies, or remote server files. - -## Decisions - -1. When a failed or draft legacy instance requests a Run distribution, Platform atomically promotes its existing endpoint to `DeploymentTargetID` and reserves `server-run-` as the generated Run endpoint. Generation is an explicit operator action and is the safe migration boundary; it avoids changing active legacy instances merely because Platform starts. -2. Component-authenticated Run hello must match the instance's dedicated Run endpoint. A component token cannot register over a shared build endpoint. -3. Run capability reporting is role-aware. A component-authenticated generated Run keeps lifecycle/file/dependency capabilities but never reports distribution build or Run self-update capabilities. A generic worker remains build-capable. -4. Source-preparation failures return a bounded category that distinguishes unavailable source, invalid source layout, and unsafe source content without exposing host paths. - -## Risks / Trade-offs - -- [A failed legacy instance needs a fresh package] → Generation performs the migration before queueing and produces a package with the new identity. -- [A stale remotely deployed package continues to reconnect] → Platform rejects it after migration because its endpoint ID no longer matches the component's dedicated identity. -- [Role-aware capabilities change scheduling] → Only build-only capabilities are removed from component packages; normal server lifecycle capability reporting is preserved. - -## Migration Plan - -1. Deploy Platform and Run changes. -2. Start the local generic build worker with `run-local-debug` and its approved source snapshot. -3. Generate Run for the failed legacy server; Platform promotes `run-local-debug` to its deployment target and embeds `server-run-` in the new Windows package. -4. Replace the old remote executable with the generated package; its stale shared-ID registration is rejected. -5. Roll back by retaining the old package and restoring the legacy binding only through an explicit recovery operation; Platform never silently maps a component token onto a shared builder endpoint. - -## Open Questions - -- None. diff --git a/openspec/changes/fix-run-build-worker-role/proposal.md b/openspec/changes/fix-run-build-worker-role/proposal.md deleted file mode 100644 index 1ac4174..0000000 --- a/openspec/changes/fix-run-build-worker-role/proposal.md +++ /dev/null @@ -1,25 +0,0 @@ -## Why - -A legacy server can use one endpoint ID for both the trusted local Run builder and its generated Windows Run. When the generated Run starts on the target host it authenticates with the server component key as the builder endpoint, displacing the local builder. Later Windows distribution builds are consequently executed without the approved local source snapshot and fail at source preparation. - -## What Changes - -- Separate a legacy server's build-target endpoint from the endpoint identity embedded in a newly generated Run before queuing the build. -- Reject component-authenticated Run registration against a shared build endpoint. -- Make a generated server Run omit build-only capabilities, including `distribution.build`. -- Preserve a clear, safe source-preparation failure reason for operational diagnosis. - -## Capabilities - -### New Capabilities - -- `run-build-worker-roles`: Separates trusted Run build workers from server-scoped generated Run workers and protects the endpoint identities used by each. - -### Modified Capabilities - -- `run-distribution-and-client-managers`: Run distribution generation assigns the generated package a server-scoped endpoint identity rather than reusing a legacy builder identity. - -## Impact - -- Affected roots: `platform/`, the independent `run/` checkout, and OpenSpec contracts. -- Existing failed legacy instances migrate on their next Run generation; no browser, plugin, or remote-host path/credential exposure is introduced. diff --git a/openspec/changes/fix-run-build-worker-role/specs/run-build-worker-roles/spec.md b/openspec/changes/fix-run-build-worker-role/specs/run-build-worker-roles/spec.md deleted file mode 100644 index 299bb6a..0000000 --- a/openspec/changes/fix-run-build-worker-role/specs/run-build-worker-roles/spec.md +++ /dev/null @@ -1,30 +0,0 @@ -## ADDED Requirements - -### Requirement: Generated Run identity is distinct from its build target - -When Platform generates a Run package for a failed or draft legacy server without `deploymentTargetId`, it SHALL reserve `server-run-` as the package endpoint identity and persist the previous endpoint as the deployment target before the distribution build job is queued. - -#### Scenario: Failed legacy server generates a new Windows Run - -- **GIVEN** a failed legacy server has `runEndpointId=run-local-debug` and no deployment target -- **WHEN** an authorized owner generates a Windows amd64 Run package -- **THEN** the build job targets `run-local-debug`, the server persists `deploymentTargetId=run-local-debug`, and the generated package is assigned `runEndpointId=server-run-` - -### Requirement: Shared builders reject component Run identity - -Platform SHALL reject a component-authenticated Run hello unless its endpoint identity equals the server's dedicated Run endpoint identity. - -#### Scenario: Old package tries to claim the builder ID - -- **GIVEN** an instance has a dedicated Run endpoint and a separate deployment target -- **WHEN** a Run component uses the instance key to register as the deployment target endpoint -- **THEN** Platform rejects the hello and leaves the build endpoint registration unchanged - -### Requirement: Generated Run does not advertise build-only capabilities - -A Run configured as a server component SHALL NOT advertise `distribution.build` or `run.self-update`. - -#### Scenario: Generated Run registers on the target host - -- **WHEN** a package with component kind `run` creates its capability report -- **THEN** lifecycle capabilities remain available and build-only capabilities are absent diff --git a/openspec/changes/fix-run-build-worker-role/specs/run-distribution-and-client-managers/spec.md b/openspec/changes/fix-run-build-worker-role/specs/run-distribution-and-client-managers/spec.md deleted file mode 100644 index 1e05f41..0000000 --- a/openspec/changes/fix-run-build-worker-role/specs/run-distribution-and-client-managers/spec.md +++ /dev/null @@ -1,11 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Run distribution generation remains asynchronous and server-scoped - -The platform SHALL create a bounded `distribution.build` job on the instance deployment target and return a `building` distribution with the real job ID. The generated Run package SHALL contain a server-scoped endpoint identity and component key; a legacy shared builder endpoint MUST be promoted to the deployment target before the job is queued. - -#### Scenario: Generate from a legacy endpoint binding - -- **GIVEN** an authorized server owner selects a legacy server whose only endpoint is an online build-capable worker -- **WHEN** the owner requests a target-matched Run distribution -- **THEN** Platform queues the build on that worker and assigns the generated package its deterministic dedicated endpoint identity diff --git a/openspec/changes/fix-run-build-worker-role/tasks.md b/openspec/changes/fix-run-build-worker-role/tasks.md deleted file mode 100644 index e287ab7..0000000 --- a/openspec/changes/fix-run-build-worker-role/tasks.md +++ /dev/null @@ -1,15 +0,0 @@ -## 1. Binding migration and registration fence - -- [x] 1.1 Promote a failed or draft legacy endpoint to a deployment target when Run generation is explicitly requested, reserving the deterministic server Run endpoint first. -- [x] 1.2 Reject component-authenticated registration that does not match the server's dedicated endpoint identity and prevent signed component sessions from claiming distribution builds. -- [x] 1.3 Add Platform regression coverage for legacy promotion, build targeting, stale package registration rejection, and signed-component claim rejection. - -## 2. Role-aware Run worker capabilities - -- [x] 2.1 Derive worker capabilities from the component role and omit build-only capabilities from generated Run packages. -- [x] 2.2 Add Run tests for generic builder and generated server Run capability reports. - -## 3. Verification - -- [x] 3.1 Run focused and full Platform/Run test suites, a Windows amd64 build-worker test, an approved-snapshot PE build proof, and isolated local debug smoke with generated artifact download evidence. -- [x] 3.2 Run `openspec validate fix-run-build-worker-role --strict` and `scripts/check-structure.sh`. diff --git a/openspec/changes/fix-scum-run-distribution-smoke/proposal.md b/openspec/changes/fix-scum-run-distribution-smoke/proposal.md deleted file mode 100644 index 64e379f..0000000 --- a/openspec/changes/fix-scum-run-distribution-smoke/proposal.md +++ /dev/null @@ -1,16 +0,0 @@ -## Why - -The local debug SCUM fixture can be registered with a reduced manifest that omits `server.run.distribution`. That makes `scum-alpha` visible and manageable, but `POST /api/v1/server-instances/scum-alpha/run/generate` is denied even though the first-party SCUM manifest declares run distribution support. - -## What Changes - -- Register the local debug SCUM plugin with the run distribution, dependency, client-manager, bridge, and remote-access declarations from the first-party manifest. -- Extend local debug smoke proof to verify `generate-run` is available for `scum-alpha`. -- Extend local debug smoke proof to generate a Windows AMD64 run distribution for `scum-alpha`. -- Make deterministic run generation recover from an already-created package artifact instead of surfacing `duplicate_resource` to the UI. -- Make repeated run self-update dispatches return the existing update job when the artifact/checksum/idempotency key match. - -## Impact - -- Affected specs: `scum-run-distribution-smoke` -- Affected code: `scripts/local-debug-smoke.sh`, `platform/service/distributions.go` diff --git a/openspec/changes/fix-scum-run-distribution-smoke/specs/scum-run-distribution-smoke/spec.md b/openspec/changes/fix-scum-run-distribution-smoke/specs/scum-run-distribution-smoke/spec.md deleted file mode 100644 index c5ae6e4..0000000 --- a/openspec/changes/fix-scum-run-distribution-smoke/specs/scum-run-distribution-smoke/spec.md +++ /dev/null @@ -1,20 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM local debug run distribution proof -The local debug smoke fixture SHALL register the first-party SCUM plugin with enough safe platform metadata for SCUM run distribution APIs to be exercised. - -#### Scenario: SCUM run generation is available -- **WHEN** local debug smoke registers `game.scum` and creates `scum-alpha` -- **THEN** `GET /api/v1/server-instances/scum-alpha/runtime/actions` MUST report `generate-run` as available - -#### Scenario: SCUM run package is generated -- **WHEN** local debug smoke calls `POST /api/v1/server-instances/scum-alpha/run/generate` for Windows AMD64 -- **THEN** the response MUST include an artifact ID and checksum without exposing raw keys, host paths, direct sockets, bearer credentials, or plugin-owned transport details - -#### Scenario: SCUM run package generation is idempotent after a partial artifact write -- **WHEN** `POST /api/v1/server-instances/scum-alpha/run/generate` is retried with the same idempotency key after the deterministic artifact already exists but the run distribution row is missing -- **THEN** the platform MUST reuse the matching artifact, create or return the run distribution, and MUST NOT return `duplicate_resource` - -#### Scenario: SCUM run self-update dispatch is idempotent -- **WHEN** `POST /api/v1/server-instances/scum-alpha/run/update` is retried with the same artifact, checksum, and idempotency key -- **THEN** the platform MUST return the existing update job and MUST NOT return `duplicate_resource` diff --git a/openspec/changes/fix-scum-run-distribution-smoke/tasks.md b/openspec/changes/fix-scum-run-distribution-smoke/tasks.md deleted file mode 100644 index c09995f..0000000 --- a/openspec/changes/fix-scum-run-distribution-smoke/tasks.md +++ /dev/null @@ -1,30 +0,0 @@ -## 1. Local Debug SCUM Runtime Proof - -- [x] 1.1 Preserve SCUM manifest run distribution declarations in local debug registration. -- [x] 1.2 Add smoke assertions that `scum-alpha` exposes `generate-run` as available. -- [x] 1.3 Add smoke proof that `scum-alpha` can generate a Windows AMD64 run distribution. -- [x] 1.4 Recover run generation retries when the deterministic artifact exists before the distribution row. -- [x] 1.5 Recover repeated run self-update dispatches for the same artifact/checksum/idempotency key. - -## 2. Verification - -- [x] 2.1 Run `bash -n scripts/local-debug-smoke.sh`. -- [x] 2.2 Run `scripts/check-structure.sh`. -- [x] 2.3 Run `openspec validate fix-scum-run-distribution-smoke --strict`. -- [x] 2.4 Run focused local debug smoke proof for the fixed SCUM run distribution path. -- [x] 2.5 Run focused backend distribution retry tests. -- [x] 2.6 Run focused run update idempotency regression tests. - -## Evidence - -- `bash -n scripts/local-debug-smoke.sh` passed. -- `scripts/check-structure.sh` passed with `structure check passed`. -- `openspec validate fix-scum-run-distribution-smoke --strict` passed; OpenSpec telemetry flush reported `ENOTFOUND edge.openspec.dev`, which did not affect validation. -- Isolated local debug smoke passed with `LOCAL_DEBUG_SELF_START=true LOCAL_DEBUG_PLATFORM_PORT=18283 LOCAL_DEBUG_WEB_PORT=5196 LOCAL_DEBUG_ROOT=/private/tmp/browser-scum-run-distribution-smoke-4 scripts/local-debug-smoke.sh`. -- Smoke evidence directory: `/private/tmp/browser-scum-run-distribution-smoke-4/smoke`. -- The passing smoke verified `scum-alpha` exposes `generate-run` as available and generated a Windows AMD64 run package without forbidden local-debug fragments. -- `go test ./service -run 'TestCoreService(GeneratesRunDistributionWithEncryptedSingletonKey|RunDistributionRetryReusesPartialArtifact|BuildsClientManagerWithDistinctKeyAndAuditsSensitiveOperations)' -count=1` passed from `platform/`. -- `go test ./api ./service -run 'Test.*(Run|Distribution|Artifact|ClientManager|Runtime)' -count=1` passed from `platform/`. -- `go test ./service -run 'TestCoreService(RunDistributionRetryReusesPartialArtifact|PushRunUpdateReusesExistingUpdateJob|BuildsClientManagerWithDistinctKeyAndAuditsSensitiveOperations)' -count=1` passed from `platform/`. -- Rerun `scripts/check-structure.sh` passed with `structure check passed`. -- Rerun `openspec validate fix-scum-run-distribution-smoke --strict` passed; OpenSpec telemetry flush reported `ENOTFOUND edge.openspec.dev`, which did not affect validation. diff --git a/openspec/changes/fix-server-card-action-menu/.openspec.yaml b/openspec/changes/fix-server-card-action-menu/.openspec.yaml deleted file mode 100644 index 64105fc..0000000 --- a/openspec/changes/fix-server-card-action-menu/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-14 diff --git a/openspec/changes/fix-server-card-action-menu/proposal.md b/openspec/changes/fix-server-card-action-menu/proposal.md deleted file mode 100644 index 418ef12..0000000 --- a/openspec/changes/fix-server-card-action-menu/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -## Why - -The server management card action menu currently opens inside the card as a large vertical command stack, which obscures operational data and makes the card feel broken. The same card stat tiles render label/value text over busy translucent materials, so placeholder values such as `--` and labels like 玩家, TPS, 延迟, and 任务 lose contrast against uploaded or magical-girl backgrounds. - -## What Changes - -- Replace the in-card runtime action stack with a compact anchored overlay that does not resize or reflow server cards. -- Keep runtime actions platform-mediated and unchanged while closing the overlay on outside click, Escape, and action selection. -- After an operator selects a runtime action, show a modal task flow with stage progress for code pull, environment install/check, dependency download, compile/build, packaging, queued jobs, and final success/failure. -- Increase server card stat readability with stronger token-driven material, label contrast, value contrast, and text shadow that works across black mecha, magical-girl, and uploaded backgrounds. -- Remove misleading whole-card pointer affordance so only actual controls look clickable. - -## Impact - -- Affected root: `platform_web/`. -- Expected files: `platform_web/pages/ServersPage.tsx`, `platform_web/pages/ServerDetailPage.tsx`, `platform_web/components/RuntimeTaskProgress.tsx`, `platform_web/theme/base.css`, and focused rendering/style contract tests if useful. -- No platform, run, plugin, lifecycle, authorization, credential, or API behavior changes. -- Verification: focused frontend tests/typecheck, `scripts/check-structure.sh`, `openspec validate fix-server-card-action-menu --strict`, and browser walkthrough of 服务器管理. diff --git a/openspec/changes/fix-server-card-action-menu/specs/server-card-action-menu-readability/spec.md b/openspec/changes/fix-server-card-action-menu/specs/server-card-action-menu-readability/spec.md deleted file mode 100644 index e59f860..0000000 --- a/openspec/changes/fix-server-card-action-menu/specs/server-card-action-menu-readability/spec.md +++ /dev/null @@ -1,47 +0,0 @@ -## ADDED Requirements - -### Requirement: Server cards use compact runtime action overlays - -The platform_web server management page SHALL expose server-card runtime actions through a compact anchored overlay that does not resize, stretch, or reflow the server card. - -#### Scenario: Operator opens runtime actions from a server card - -- **WHEN** an operator selects the 运行操作 trigger on a server card -- **THEN** runtime actions MUST appear in a compact overlay anchored near the trigger instead of as a tall in-card vertical command stack -- **AND** the server card layout, neighboring cards, metrics, progress bars, title, and status badge MUST remain structurally stable - -#### Scenario: Operator dismisses runtime actions - -- **WHEN** the runtime action overlay is open -- **THEN** outside click, Escape, and selecting an action MUST dismiss the overlay -- **AND** the action dispatch MUST continue to use the existing platform-mediated runtime APIs - -### Requirement: Runtime actions show task progress dialogs - -The platform_web server management and server detail runtime action surfaces SHALL show a modal task flow after an operator selects a runtime action so the operator can see meaningful progress instead of a silent click-and-finish interaction. - -#### Scenario: Operator generates a run package - -- **WHEN** an operator selects 生成 run for a server -- **THEN** platform_web MUST open a modal task dialog that shows stage progress for code pull, environment install/check, dependency download, compile/build, package finalization, and terminal success or failure -- **AND** the dialog MUST include a readable progress meter, current stage, recent task log lines, and final artifact/job summary when the platform API returns - -#### Scenario: Operator starts a queued runtime maintenance action - -- **WHEN** an operator selects dependency check, dependency install, push update, live logs, or historical logs from the runtime action surfaces -- **THEN** platform_web MUST show a modal task dialog with action-specific staged progress and final queued job or navigation status -- **AND** the dialog MUST keep sensitive runtime credentials, raw host paths, direct sockets, and secret material hidden - -### Requirement: Server card metrics remain readable over themed backgrounds - -Server card metric tiles SHALL keep labels and values readable across black mecha, magical-girl, and uploaded-background states. - -#### Scenario: Metrics are unavailable or pending - -- **WHEN** 玩家, TPS, 延迟, or 任务 values render as pending or unavailable placeholders such as `…` or `--` -- **THEN** both the label and value text MUST remain legible over the card background and selected workspace background - -#### Scenario: Custom background is active - -- **WHEN** an uploaded background is active behind server cards -- **THEN** metric tiles MUST use stronger surface material, borders, and text contrast so operational data is not visually swallowed by the background image diff --git a/openspec/changes/fix-server-card-action-menu/tasks.md b/openspec/changes/fix-server-card-action-menu/tasks.md deleted file mode 100644 index a9088c3..0000000 --- a/openspec/changes/fix-server-card-action-menu/tasks.md +++ /dev/null @@ -1,31 +0,0 @@ -## 1. Server Card Interaction Fix - -- [x] 1.1 Replace the `
` runtime action stack with a compact overlay that is anchored to the trigger and does not reflow the card. -- [x] 1.2 Close the overlay on outside click, Escape, and action selection. -- [x] 1.3 Keep runtime action dispatch semantics unchanged. -- [x] 1.4 Show a runtime task progress dialog after action selection with staged progress, logs, and terminal success/failure state. -- [x] 1.5 Reuse the same progress dialog from server detail runtime distribution actions. - -## 2. Server Card Readability Fix - -- [x] 2.1 Strengthen `.server-card-stat` label/value contrast for 玩家, TPS, 延迟, 任务, and placeholder values. -- [x] 2.2 Preserve the shared black-mecha / magical-girl console visual system and uploaded-background readability. -- [x] 2.3 Remove misleading whole-card click affordance while keeping explicit detail/menu controls. - -## 3. Verification - -- [x] 3.1 Run focused frontend tests and typecheck. -- [x] 3.2 Run `scripts/check-structure.sh`. -- [x] 3.3 Run `openspec validate fix-server-card-action-menu --strict`. -- [ ] 3.4 Browser-walkthrough 服务器管理 and verify the menu is compact, card stats are readable, action selection opens the progress dialog, and no metrics/progress/status are covered. - -## Evidence - -- `npm run typecheck` passed. -- `npm test -- ConsolePages.test.tsx ServerDetailPage.test.tsx base-css.test.js` passed with 22 tests. -- `npm test` passed with 68 tests. -- `npm run build` passed. -- `go test ./api ./service -run 'Test.*(Run|Distribution|Artifact|ClientManager|Runtime)' -count=1` passed from `platform/`, proving backend run distribution, artifact download, update, and client-manager paths are implemented. -- `scripts/check-structure.sh` passed with `structure check passed`. -- `openspec validate fix-server-card-action-menu --strict` passed. OpenSpec telemetry flush failed with `ENOTFOUND edge.openspec.dev`, which did not affect validation. -- Browser walkthrough is still pending because the Browser plugin reported no available browser backends (`agent.browsers.list()` returned `[]`). diff --git a/openspec/changes/fix-server-deployment-trigger/.openspec.yaml b/openspec/changes/fix-server-deployment-trigger/.openspec.yaml deleted file mode 100644 index e8209ff..0000000 --- a/openspec/changes/fix-server-deployment-trigger/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-28 diff --git a/openspec/changes/fix-server-deployment-trigger/design.md b/openspec/changes/fix-server-deployment-trigger/design.md deleted file mode 100644 index d754d0c..0000000 --- a/openspec/changes/fix-server-deployment-trigger/design.md +++ /dev/null @@ -1,39 +0,0 @@ -## Context - -The Platform already validates and dispatches `POST /api/v1/server-instances/{id}/deploy`; it accepts a draft or failed server only after its dedicated Run endpoint is registered. The server detail page exposes start, stop, and edit actions, but never calls that deployment API. As a result, a newly registered Run remains correctly idle and a failed server cannot be retried through the console. - -## Goals / Non-Goals - -**Goals:** - -- Make the existing deployment transition available to an authorized operator from the server detail header. -- Use the current server config version and a unique idempotency key, then display ordinary operation feedback and refresh state. -- Make the action unavailable while installation is already active or the state is not deployable. - -**Non-Goals:** - -- Do not change the backend API, deployment plan, Run protocol, SCUM install sequence, or automatic-start behavior. -- Do not expose protected directories, commands, tokens, or remote connectivity details. -- Do not change the separate build target / dedicated Run identity boundary. - -## Decisions - -- Add the control beside existing lifecycle controls rather than auto-dispatching when a Run heartbeats. Registration proves connectivity only; automatic installation would make starting a downloaded executable perform a material remote write without a final operator action. -- Reuse `platformApiClient.deployServerInstance` rather than duplicating request logic. This preserves backend authorization, version fencing, idempotency, and job projection behavior. -- Reuse the existing operation store and `refresh()` callback so the detail page follows the same feedback pattern as start/stop and immediately shows the job state. - -## Risks / Trade-offs - -- [A stale page submits an outdated version] → The backend's `expectedConfigVersion` check rejects it; the UI refreshes after the error. -- [Users mistake registration for completed installation] → The action label explicitly distinguishes Deploy from Start, and the queued job result is shown through the standard task feedback. -- [A duplicate click creates duplicate work] → A fresh idempotency key is submitted and the backend enforces its lifecycle idempotency rules. - -## Migration Plan - -1. Deploy the frontend change with the existing Platform API. -2. Open a failed or draft server whose dedicated Run is online and select Deploy / Retry deploy. -3. Confirm the resulting install job is assigned to the dedicated endpoint. Rollback only removes the UI control; no data migration is required. - -## Open Questions - -None. diff --git a/openspec/changes/fix-server-deployment-trigger/proposal.md b/openspec/changes/fix-server-deployment-trigger/proposal.md deleted file mode 100644 index 3cd3940..0000000 --- a/openspec/changes/fix-server-deployment-trigger/proposal.md +++ /dev/null @@ -1,24 +0,0 @@ -## Why - -The dedicated SCUM Run can register successfully, but a failed or draft server has no visible Platform action that submits its existing deployment definition. Operators therefore see an idle Run startup log and cannot advance the installation workflow. - -## What Changes - -- Add an explicit Deploy / Retry deploy control to the server detail actions for draft and failed servers. -- Submit the existing protected deployment definition through the existing authenticated deployment API, using the current config version and a fresh idempotency key. -- Present the queued deployment result in the normal operation feedback and refresh the server/job view. - -## Capabilities - -### New Capabilities - -- `server-deployment-trigger`: Operator-facing dispatch of an already-configured draft or failed server deployment after its dedicated Run registers. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform_web/pages/ServerDetailPage.tsx` and its focused UI tests. -- Reuses the existing `POST /api/v1/server-instances/{id}/deploy` contract; no new server API, remote access, credentials, or host data are introduced. diff --git a/openspec/changes/fix-server-deployment-trigger/specs/server-deployment-trigger/spec.md b/openspec/changes/fix-server-deployment-trigger/specs/server-deployment-trigger/spec.md deleted file mode 100644 index 6e51875..0000000 --- a/openspec/changes/fix-server-deployment-trigger/specs/server-deployment-trigger/spec.md +++ /dev/null @@ -1,21 +0,0 @@ -## ADDED Requirements - -### Requirement: Deployable server details expose an explicit deployment trigger -The management console SHALL expose an authorized Deploy action for a server in `draft` or `failed` state when a deployment definition is present. The control MUST remain unavailable for non-deployable states and while an installation action is pending. - -#### Scenario: Retry a failed server after its dedicated Run registers -- **WHEN** an operator opens a failed SCUM server whose dedicated Run endpoint is online -- **THEN** the detail actions expose a Retry deploy control -- **AND** the control is distinct from Start and does not expose protected deployment inputs - -#### Scenario: Active installation does not offer a duplicate trigger -- **WHEN** a server is in `installing` state or its deployment operation is pending -- **THEN** the console does not allow another deployment submission - -### Requirement: Deployment trigger uses the existing fenced lifecycle dispatch -The management console SHALL submit deployment through the existing server deployment API with the instance's current config version and an idempotency key, then refresh the server detail after acceptance or failure. - -#### Scenario: Deployment is accepted -- **WHEN** the operator activates Deploy for an eligible server -- **THEN** the console submits the current expected config version and a unique idempotency key to the existing deployment API -- **AND** it reports the queued job through the standard operation feedback and refreshes the detail state diff --git a/openspec/changes/fix-server-deployment-trigger/tasks.md b/openspec/changes/fix-server-deployment-trigger/tasks.md deleted file mode 100644 index 9ed9577..0000000 --- a/openspec/changes/fix-server-deployment-trigger/tasks.md +++ /dev/null @@ -1,9 +0,0 @@ -## 1. Server detail deployment action - -- [x] 1.1 Add an explicit Deploy / Retry deploy handler that invokes the existing fenced API with current config version and operation feedback. -- [x] 1.2 Render the action only for deployable draft or failed instances and prevent duplicate submission while pending. - -## 2. Verification - -- [x] 2.1 Extend focused server-detail tests to cover the deploy trigger and its state guards. -- [x] 2.2 Run frontend tests, the structure check, and strict OpenSpec validation. diff --git a/openspec/changes/follow-current-supervised-log-session/.openspec.yaml b/openspec/changes/follow-current-supervised-log-session/.openspec.yaml deleted file mode 100644 index 4af8641..0000000 --- a/openspec/changes/follow-current-supervised-log-session/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-14 diff --git a/openspec/changes/follow-current-supervised-log-session/design.md b/openspec/changes/follow-current-supervised-log-session/design.md deleted file mode 100644 index 24abd21..0000000 --- a/openspec/changes/follow-current-supervised-log-session/design.md +++ /dev/null @@ -1,75 +0,0 @@ -## Context - -Run currently persists a supervised process identity and forwards plugin-declared `process.stdout` and `process.stderr` through its durable spool. Autonomous lifecycle stream IDs are stable per Run endpoint, server, and stream key, so Platform cannot separate a later process start from the previous generation. The terminal then lists and replays all server streams, including old jobs, before it listens for SSE appends. - -Platform remains the authorization and persistence boundary. Run remains the sole authority for the supervised process. The design must expose only a logical session identifier and timestamps, never output paths, PIDs, command lines, credentials, or direct process handles. - -## Goals / Non-Goals - -**Goals:** - -- Follow only the current plugin-declared supervised process output in the live terminal. -- Preserve the same session when Run restarts and resumes an already-running supervised process. -- Switch an open terminal atomically when the supervised process starts a new generation. -- Keep bounded recent replay for the selected session and keep all older streams readable as explicit history. -- Keep RCON and any future plugin command transport independent of the log data path. - -**Non-Goals:** - -- Expose browser shell access, host paths, raw sockets, or process stdin by default. -- Infer game logs from arbitrary files or special-case SCUM, Minecraft, or another game. -- Delete, migrate, or reinterpret legacy log bodies. - -## Decisions - -### Process-generation session is generated and persisted by Run - -`ProcessIdentity` receives a random/logical session ID and its start timestamp when a new managed process is started. The identity is journaled, so a Run restart resumes tailing and continues using the identical session for the still-running process. Starting a replacement process generates a new session. - -The session ID is added to process log batches and autonomous process stream IDs. This avoids sequence collisions and gives Platform a durable grouping key. A stable stream ID with an inferred time was rejected because delayed spool upload could make an old process appear newer. - -### Platform selects the current session only from supervised process streams - -Platform persists the logical session ID and process-start timestamp with each stream. Run identifies an observed generation as `log-session:`, and a persisted session is eligible as current only while the bound Run endpoint is online, Platform's latest Run-reported supervised-process fact is `running`, and that fact names the same session. This generation binding prevents a delayed batch from an older or not-yet-observed process from becoming current merely because it has a newer timestamp. Among the matching session's process streams, timestamp and deterministic ID ordering remain useful only for stable replay ordering. Only `source=process` streams with a non-empty session ID participate. Legacy, job, file-tail, and management-program logs remain historical and cannot displace a current terminal session. - -Run reports observations for every process it supervises, including servers handled by a general worker rather than only a generated single-server Run. Platform publishes process-state changes into the existing server log subscription. A stopped, exited, or failed fact emits an empty session boundary; a later running fact re-evaluates the persisted streams. Run endpoint disconnect alone does not end a session because the game process may survive a Run restart, but a newly opened terminal treats an offline endpoint as having no proven current session until Run reconnects and reports the process fact. - -Dispatching a new start clears Platform's previous managed-process binding before the job is queued. Historical output from the prior generation therefore cannot reappear during the interval between desired start intent and Run's first observation for the replacement generation. - -Using the newest arbitrary stream was rejected because a file backfill or command result is not evidence of the process that the terminal must follow. - -### SSE carries an explicit session boundary - -The server log event endpoint begins with a `session` event, then exposes only the selected session's streams and a bounded replay. On each incoming process event Platform re-evaluates the active session. When it changed, Platform emits a new `session` event, its stream metadata, and a new selected-session replay before normal appends. The client clears the live buffer on the session boundary and remains connected; it never has to guess based on timestamps. - -Re-opening a new EventSource on each restart was rejected because it races with output and leaves the old terminal contents visible while reconnecting. - -### Durable source cursors make resumed file reads idempotent - -Each process-output append carries its generation-local source-file cursor into the durable spool. The spool commits sequence allocation and the source cursor with the batch, restores both from pending segments, and treats a replayed cursor as already committed. This closes the crash window between a durable spool append and the process journal offset checkpoint. - -The process journal retains superseded generations until both stdout and stderr files are fully drained. Starting generation B therefore cannot erase generation A recovery state if Run exits before A's final output is spooled. Existing journals without a session ID are upgraded in place for a still-running process so deployment of the new Run does not require restarting the game process. - -### History is explicitly requested - -The existing log-stream list/cursor contracts remain the history source. The terminal includes a deliberate history mode that reads selected historical stream cursors; it is not fed by the live SSE endpoint. This retains operator access without contaminating the live view. - -## Risks / Trade-offs - -- [Old Run versions emit no session ID] → Platform keeps their logs accessible in history but excludes them from live-follow selection; deployment is backward-safe and becomes live when Run is updated. -- [A process emits before the initial SSE replay completes] → line identity is stream ID plus sequence and client-side merge de-duplicates replay/live overlap. -- [Run exits after spooling a line but before checkpointing its file offset] → the durable source cursor makes the repeated read idempotent. -- [A new process starts before the old output files finish draining] → Run journals the retired generation until both channels are complete. -- [Session metadata tampering] → Platform accepts it only through the existing authenticated Run ingest channel and requires it to match the stream's immutable metadata. -- [Unbounded current logs] → live replay remains bounded while the full selected stream remains available through explicit history cursors. - -## Migration Plan - -1. Deploy Platform so it accepts session metadata and treats old streams as history. -2. Deploy Run so new managed process starts generate session-scoped stream IDs and resumptions preserve the persisted session. -3. Deploy the frontend terminal that understands `session` SSE boundaries and offers explicit history. -4. Rollback is safe: historical streams and batches remain immutable; an older frontend ignores the additional SSE event and Run can continue uploading session-scoped streams to the compatible Platform. - -## Open Questions - -None. The session scope is the plugin-declared process output, and the terminal's default is the current session. diff --git a/openspec/changes/follow-current-supervised-log-session/proposal.md b/openspec/changes/follow-current-supervised-log-session/proposal.md deleted file mode 100644 index f0da5d8..0000000 --- a/openspec/changes/follow-current-supervised-log-session/proposal.md +++ /dev/null @@ -1,31 +0,0 @@ -## Why - -The server terminal currently replays recent entries across every log stream ever created for an instance. That makes an open terminal appear stuck on stale startup output and leaves it unable to distinguish the process currently supervised by Run from a prior Run or process generation. - -Operators need a terminal that continuously follows the current managed process even when Run or the process restarts, while keeping historical logs available deliberately and keeping game command execution separate from log transport. - -## What Changes - -- Define a current supervised-log session identity for each Run-managed process generation and attach it to process stdout/stderr log streams. -- Make the live terminal subscribe to the active supervised-log session by default, including a bounded recent replay from that session only. -- Notify terminal subscribers when the active session changes so an already-open terminal automatically replaces the old session output with the new process generation and continues following it. -- Clear the live terminal when Run reports that the supervised process stopped or exited, and restore the same session when a restarted Run confirms that the process survived. -- Retain historical logs as an explicit history view instead of mixing them into the live terminal. -- Preserve RCON and other plugin-declared command transports solely for command dispatch; they are not log sources. -- Extend the local end-to-end smoke coverage with a process/Run generation switch and output markers proving that the terminal feed follows the new generation. - -## Capabilities - -### New Capabilities - -- `current-supervised-log-session`: identifies and follows the current plugin-declared supervised process stdout/stderr session, including generation changes and separate explicit history access. - -### Modified Capabilities - - - -## Impact - -- Affected code: Run process supervision and log metadata, Platform log ingest/domain/API/SSE handling, the server-management terminal API client and drawer, and the local debug smoke path. -- Affected API: Run log ingest, supervised-process observations, and server log SSE gain a bounded session identity/filtering contract; a history-only query is exposed or retained separately. -- Compatibility: existing persisted log streams remain readable through history, but no longer appear by default in the live terminal. diff --git a/openspec/changes/follow-current-supervised-log-session/specs/current-supervised-log-session/spec.md b/openspec/changes/follow-current-supervised-log-session/specs/current-supervised-log-session/spec.md deleted file mode 100644 index b2f3827..0000000 --- a/openspec/changes/follow-current-supervised-log-session/specs/current-supervised-log-session/spec.md +++ /dev/null @@ -1,76 +0,0 @@ -## ADDED Requirements - -### Requirement: Live terminal follows the current supervised process session -The system SHALL identify each newly started Run-supervised process generation with a durable logical session ID, and SHALL associate its plugin-declared `process.stdout` and `process.stderr` streams with that session. A Run restart that resumes the same running process SHALL retain that session ID. - -#### Scenario: Process output is collected independently of the terminal -- **WHEN** a plugin-declared supervised process writes stdout or stderr while no browser terminal is open -- **THEN** Run SHALL durably spool and upload that output under the process's logical session without opening an RCON connection or depending on browser state - -#### Scenario: Run resumes a running process after restart -- **WHEN** Run restarts while its persisted supervised process is still running -- **THEN** Run SHALL resume collecting output with the persisted process session ID and the terminal SHALL continue following that session - -#### Scenario: Run crashes between durable append and output checkpoint -- **WHEN** a process line is already durable in the local spool but Run restarts before its output-file offset checkpoint is persisted -- **THEN** Run SHALL recognize the repeated source cursor and SHALL NOT allocate or upload a duplicate log entry - -#### Scenario: A replacement starts before the prior output drain completes -- **WHEN** Run starts a replacement process while the prior generation still has unread stdout or stderr -- **THEN** Run SHALL retain and resume the prior generation's drain state independently of the replacement generation - -#### Scenario: Existing process journal predates session metadata -- **WHEN** an upgraded Run loads a legacy journal for a supervised process that is still alive -- **THEN** Run SHALL assign and persist a session for that same process without requiring the game process to restart - -### Requirement: Live SSE exposes only the active supervised session -The server log SSE endpoint SHALL select the supervised-process session named by the current `running` process fact from an online bound Run endpoint, publish an explicit session boundary, and replay only a bounded set of entries from that selected session. The process fact SHALL identify the generation as `log-session:`. The endpoint SHALL exclude legacy, completed-job, file-tail, management-program, unbound, and older supervised-session entries from the default live feed. - -#### Scenario: Terminal opens after prior process generations -- **WHEN** an instance has historical job streams and older process sessions and an operator opens the terminal -- **THEN** the endpoint SHALL publish only the active session metadata and its bounded recent output before live appends - -#### Scenario: No current session exists -- **WHEN** no session-scoped supervised process stream has been accepted for an instance -- **THEN** the endpoint SHALL publish a ready empty live session and SHALL NOT replay unrelated historical streams - -#### Scenario: Persisted session belongs to a stopped process -- **WHEN** the newest persisted session belongs to a process that Run reported stopped, exited, or failed -- **THEN** a newly opened terminal SHALL receive an empty live session and SHALL expose the persisted output only through history - -#### Scenario: Output arrives before its generation observation -- **WHEN** session-scoped process output is accepted before Run reports a matching `log-session:` running observation -- **THEN** the endpoint SHALL retain the output as history and SHALL NOT select it as the current live session until that matching observation arrives - -#### Scenario: A new start is dispatched from a stopped instance -- **WHEN** Platform accepts a new start request for an instance with a previous process-session binding -- **THEN** Platform SHALL clear the previous binding before dispatch so historical output cannot become current while the replacement generation is unobserved - -### Requirement: Open terminal changes session without reconnection -The SSE endpoint SHALL detect a newer supervised-process session on accepted process output and SHALL emit a new session boundary, its stream metadata, and the new session replay to existing subscribers before sending subsequent output for that session. - -#### Scenario: Process restarts while terminal is open -- **WHEN** a replacement supervised process writes its first stdout or stderr entry while an operator's terminal is open -- **THEN** the terminal SHALL discard the old live buffer, label the new current session, and display the new generation's output without the operator reopening the terminal - -#### Scenario: Current process exits while terminal is open -- **WHEN** Run reports that the current supervised process stopped, exited, or failed -- **THEN** the same SSE connection SHALL emit an empty session boundary and the terminal SHALL remove the ended session from its live buffer - -#### Scenario: Run reconnects to a surviving process -- **WHEN** a Run endpoint reconnects and reports that its persisted supervised process is still running -- **THEN** the same SSE connection SHALL reselect that process's existing session without creating a replacement session - -### Requirement: Historical logs remain explicit and separate -The system SHALL retain persisted log streams and cursor queries for historical inspection. The terminal SHALL expose history only through an explicit operator action or view and SHALL NOT merge it into the current live buffer. - -#### Scenario: Operator reviews an older stream -- **WHEN** an operator explicitly selects a historical log stream -- **THEN** the terminal SHALL fetch and display that stream's retained cursor entries separately from the live session - -### Requirement: Command transports are not log sources -The system SHALL use RCON or another plugin-declared command transport only to execute a requested command. It SHALL collect live terminal output only through plugin-declared log sources, prioritizing the supervised process stdout/stderr channels. - -#### Scenario: Operator sends an RCON command -- **WHEN** an operator submits a SCUM management command -- **THEN** the command SHALL use the protected RCON command path while live terminal output continues to arrive independently from the supervised process stream diff --git a/openspec/changes/follow-current-supervised-log-session/tasks.md b/openspec/changes/follow-current-supervised-log-session/tasks.md deleted file mode 100644 index a11ece1..0000000 --- a/openspec/changes/follow-current-supervised-log-session/tasks.md +++ /dev/null @@ -1,28 +0,0 @@ -## 1. Run process-session protocol - -- [x] 1.1 Persist a new logical session ID and start timestamp for each newly supervised process while retaining it during Run output resumption. -- [x] 1.2 Include session metadata in durable process log batches and make autonomous process stream IDs generation-scoped without changing non-process/job log behavior. -- [x] 1.3 Add Run tests for new process generations and Run restart/resumption retaining the session. -- [x] 1.4 Make source-file replay idempotent across crashes, retain undrained retired generations, atomically replace aggregated spool segments, and upgrade live legacy journal entries. - -## 2. Platform active-session feed - -- [x] 2.1 Persist and validate immutable session metadata on log streams, including session-scoped Run stream recognition. -- [x] 2.2 Select active supervised-process streams and add session-boundary SSE events with selected-session-only replay and live filtering. -- [x] 2.3 Add Platform service/API tests covering stale history exclusion, new-session switches, and command/log separation. -- [x] 2.4 Gate initial current-session replay on online Run process facts and publish empty/recovered session boundaries on supervised process observations. - -## 3. Terminal live and history views - -- [x] 3.1 Extend frontend log contracts and SSE parsing for session boundaries. -- [x] 3.2 Make the terminal clear and follow a switched session automatically, and provide an explicit historical-stream view. -- [x] 3.3 Add frontend tests for current-session replay, live session switch, and separated history. - -## 4. End-to-end verification - -- [x] 4.1 Extend local debug smoke to prove first-generation output, process/Run generation switch, and post-switch terminal output. -- [ ] 4.2 Run OpenSpec validation, focused unit tests, structural validation, and the complete local smoke; record any external-environment blocker precisely. - - 2026-08-15 verification passed: `openspec validate follow-current-supervised-log-session --strict`; `go test ./api ./service ./domain ./dto ./validator` in `platform/`; `go test ./runtime ./spool ./protocol` in `run/`; `npm --prefix platform_web test -- ServerManagementTerminalDrawer client schemas/serverManagement`; `scripts/check-structure.sh`; `bash -n scripts/local-debug/smoke.sh`. - - 2026-08-15 complete local smoke command attempted: `LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-current-session-smoke LOCAL_DEBUG_PLATFORM_PORT=18198 LOCAL_DEBUG_WEB_PORT=5192 LOCAL_DEBUG_SELF_START=true scripts/dev-smoke.sh`. - - Current supervised log-session proof passed inside that smoke, including generation A replay, Run restart/resumption, generation B switch on the same SSE connection, and explicit-history exclusion; evidence file: `/private/tmp/browser-local-debug-current-session-smoke/smoke/current-log-session-verification.json`. - - External blocker: the complete smoke later failed in the platform Docker distribution builder at the host-native Run generation step because `go mod download` timed out fetching `github.com/dustin/go-humanize@v1.0.1` from `proxy.golang.org` (`dial tcp 142.251.33.209:443: i/o timeout`). diff --git a/openspec/changes/harden-log-artifact-channel-isolation/.openspec.yaml b/openspec/changes/harden-log-artifact-channel-isolation/.openspec.yaml deleted file mode 100644 index 8cceb8d..0000000 --- a/openspec/changes/harden-log-artifact-channel-isolation/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-08 diff --git a/openspec/changes/harden-log-artifact-channel-isolation/design.md b/openspec/changes/harden-log-artifact-channel-isolation/design.md deleted file mode 100644 index 73eb77d..0000000 --- a/openspec/changes/harden-log-artifact-channel-isolation/design.md +++ /dev/null @@ -1,66 +0,0 @@ -## Context - -The platform/run architecture already separates control registration, job lifecycle calls, durable log ingest, and artifact transfer into typed routes and protocol packages. Prior changes proved those channels individually, and the lifecycle proof showed that real plugin operations can route through platform-owned lifecycle APIs into run jobs. - -This change hardens the cross-channel behavior. The important failure mode is not just malformed payloads; it is starvation under concurrent work. A large artifact or file transfer must not delay control heartbeat, job acknowledgement, job result delivery, or durable log spool upload. Likewise, retry queues must stay independently bounded so a blocked artifact transfer cannot consume the execution path needed for log ingest or job completion. - -## Goals / Non-Goals - -**Goals:** -- Prove run-side scheduling keeps control heartbeat, job ack/result, log upload, and artifact/file transfer on independently bounded paths. -- Prove platform APIs validate and mutate state independently when log, artifact, job, and control requests interleave. -- Add focused tests that simulate slow or large artifact/file work while verifying timely heartbeat, job ack/result, and log acknowledgement. -- Document the channel priority and non-starvation invariants in run/platform protocol docs. -- Preserve the existing channel APIs unless implementation reveals a contract gap that must be made explicit in the spec. - -**Non-Goals:** -- Do not add new plugin-facing transport or direct run access. -- Do not add a browser UI flow unless implementation discovers an existing platform_web surface incorrectly exposes channel details. -- Do not redesign storage backends, introduce external queues, or require distributed infrastructure. -- Do not change artifact/log/job payload semantics except where needed to enforce bounded isolation. - -## Decisions - -1. Keep isolation proof local to run/platform packages before adding broader e2e tooling. - - The current risk lives in queueing, retry, route handling, and worker scheduling. Package-level tests can deterministically simulate slow artifact uploads, retryable platform failures, and interleaved requests without relying on brittle timing from a full browser stack. A later browser acceptance suite can reuse this confidence without becoming the primary proof. - - Alternative considered: start with a full local platform/run/browser smoke. That gives nice operator evidence but is weaker for starvation because browser timing is noisy and harder to make deterministic. - -2. Treat control and job lifecycle calls as high-priority bounded work. - - Heartbeats, job claim/ack/progress/result, and cancellation/reconcile calls remain small JSON payloads. They must never carry artifact chunks, file bodies, or large inline logs. Tests should assert that delayed artifact/file uploads cannot prevent these calls from completing. - - Alternative considered: one shared retry worker for all run-to-platform calls. That is simpler, but a stuck artifact transfer could monopolize retries and delay lifecycle visibility. - -3. Keep log ingest durable and independently retryable. - - The log spool already persists batches until platform acknowledgement. This change should assert that log batch selection, upload, ack handling, and retry bookkeeping stay independent from artifact chunk retry queues and job result submission. - - Alternative considered: merge log and artifact retry state because both are upload queues. That would blur priority boundaries and make it easier for large artifact payloads to starve small log acknowledgements. - -4. Verify platform state isolation with interleaved service/API tests. - - Platform tests should interleave control heartbeat, job ack/result, log batch ingest, and artifact transfer requests for the same run endpoint. Success means each route validates only its own contract, mutates only its own state, and preserves idempotency when requests are retried or reordered within valid channel rules. - - Alternative considered: only test run-side clients. That would miss platform-side cross-route coupling, such as artifact completion accidentally blocking log acknowledgement state. - -## Risks / Trade-offs - -- Timing-sensitive tests become flaky -> Use deterministic fakes, channels, contexts, and bounded wait helpers instead of wall-clock sleeps wherever possible. -- Hardening may reveal that current worker scheduling is too serial -> Introduce small, explicit channel executors or queue limits rather than broad worker rewrites. -- Additional docs can drift -> Keep docs close to `run/protocol/` and `platform/protocol/` route contracts, and update them in the same implementation task as tests. -- Full starvation proof can become too broad -> Scope the first pass to platform/run package behavior and exact commands in `tasks.md`; leave browser-wide automation to the later acceptance-suite queue item. - -## Migration Plan - -1. Add failing tests for platform and run channel isolation around existing APIs and queues. -2. Adjust run scheduling, retry queues, or client sequencing only where tests prove coupling. -3. Update protocol documentation with the enforced invariants. -4. Run platform/run tests, structure check, and strict OpenSpec validation. - -Rollback is straightforward because expected changes are test and scheduling hardening around existing APIs. If a scheduling change regresses behavior, revert that implementation while keeping the new tests as the contract for the corrected approach. - -## Open Questions - -- None currently. The implementation should stay within `run/` and `platform/` unless a failing test proves a shared contract needs a spec update. diff --git a/openspec/changes/harden-log-artifact-channel-isolation/proposal.md b/openspec/changes/harden-log-artifact-channel-isolation/proposal.md deleted file mode 100644 index 8928fbb..0000000 --- a/openspec/changes/harden-log-artifact-channel-isolation/proposal.md +++ /dev/null @@ -1,25 +0,0 @@ -## Why - -Log ingest, artifact/file transfer, control heartbeat, and job ack/result delivery already exist as separate platform/run channels, but the current evidence mostly proves each channel in isolation. The next risk is starvation under load: a large artifact or file operation must not delay heartbeat, lifecycle acknowledgement, job result delivery, or durable log upload. - -## What Changes - -- Add channel-isolation requirements that define priority, bounded payloads, retry behavior, and non-starvation guarantees across run/platform channels. -- Add run-side concurrency and queue tests proving large artifact/file work cannot block control heartbeat, job ack/result submission, or log spool upload. -- Add platform service/API tests proving artifact/log/job/control endpoints preserve independent validation, state mutation, and idempotency under interleaved requests. -- Add a local verification command set that exercises platform and run test suites plus strict OpenSpec validation. -- No breaking API changes are expected; the change hardens behavior and verification around existing channel contracts. - -## Capabilities - -### New Capabilities -- `log-artifact-channel-isolation`: Defines cross-channel non-starvation, bounded-transfer, durable-retry, and verification guarantees for run/platform control, job, log, artifact, and file operations. - -### Modified Capabilities -- None. - -## Impact - -- Affected roots: `run/` and `platform/`. -- Affected areas: run worker scheduling, platform client calls, log spool retry, artifact/file queue retry, platform run-facing APIs, service tests, API tests, and protocol documentation. -- Validation impact: requires focused run/platform concurrency tests, existing package tests, `scripts/check-structure.sh`, and `openspec validate harden-log-artifact-channel-isolation --strict`. diff --git a/openspec/changes/harden-log-artifact-channel-isolation/specs/log-artifact-channel-isolation/spec.md b/openspec/changes/harden-log-artifact-channel-isolation/specs/log-artifact-channel-isolation/spec.md deleted file mode 100644 index 3b19176..0000000 --- a/openspec/changes/harden-log-artifact-channel-isolation/specs/log-artifact-channel-isolation/spec.md +++ /dev/null @@ -1,56 +0,0 @@ -## ADDED Requirements - -### Requirement: Run channels preserve non-starvation under large transfers -The run executor SHALL keep control heartbeat, job acknowledgement, job result delivery, and log batch upload on bounded execution paths that are not blocked by large artifact or file transfer work. - -#### Scenario: Artifact upload does not block lifecycle calls -- **WHEN** a run endpoint is uploading or retrying a large artifact or file transfer -- **THEN** control heartbeat, job acknowledgement, job progress, cancellation polling, reconciliation, and terminal job result calls MUST remain able to complete through their typed platform client methods without waiting for the transfer payload to finish - -#### Scenario: Log upload continues during transfer pressure -- **WHEN** artifact or file transfer chunks are queued, slow, or retrying -- **THEN** the run log spool MUST still select bounded log batches, upload them through the log ingest client, and remove acknowledged batches independently from artifact/file queue state - -### Requirement: Platform routes mutate only their own channel state -The platform SHALL handle interleaved control, job, log, artifact, and file requests for the same run endpoint without one channel accepting another channel's payload or mutating another channel's state. - -#### Scenario: Interleaved valid requests succeed independently -- **WHEN** a registered run endpoint interleaves valid heartbeat, job ack/result, log batch ingest, and artifact chunk or completion requests -- **THEN** each route MUST validate its own typed DTO, update only the corresponding control/job/log/artifact state, and return the same acknowledgement semantics as if the requests were sent without interleaving - -#### Scenario: Heavy payload is rejected from lightweight routes -- **WHEN** a control, job, or log route receives an artifact chunk, file body, host path, raw credential, direct socket, or other transport payload owned by another channel -- **THEN** the platform MUST reject the request as a JSON validation error and MUST NOT mutate control session, job lifecycle, log acknowledgement, or artifact state - -### Requirement: Retry queues remain independently bounded -The run executor SHALL keep log retry state and artifact/file retry state independently bounded and independently acknowledged. - -#### Scenario: Artifact retry backlog does not consume log retry state -- **WHEN** artifact or file chunks remain unacknowledged after platform upload failures -- **THEN** the artifact/file retry queue MUST retain those chunks without preventing log spool retry listing, log batch upload, or acknowledged log batch removal - -#### Scenario: Log retry backlog does not consume artifact retry state -- **WHEN** log batches remain unacknowledged after platform ingest failures -- **THEN** the log spool MUST retain those batches without preventing artifact/file retry listing, chunk upload, or acknowledged artifact chunk removal - -### Requirement: Job terminal results remain bounded and prioritized -The run job channel SHALL submit terminal job results as bounded metadata and result references, not inline logs, artifact chunks, file bodies, host paths, raw credentials, or direct sockets. - -#### Scenario: Terminal result arrives while transfer is active -- **WHEN** a job finishes while artifact/file transfer work is still active or retrying -- **THEN** run MUST submit the terminal job result through the job result endpoint with bounded result metadata and the platform MUST accept or reject it only according to job lease and idempotency rules - -#### Scenario: Duplicate terminal result remains idempotent under pressure -- **WHEN** run retries an equivalent terminal job result while log and artifact retries are also pending -- **THEN** platform MUST return the accepted idempotent terminal result response and MUST NOT duplicate logs, chunks, artifacts, or unrelated job metadata - -### Requirement: Channel isolation is documented and verified -The change SHALL document the enforced priority and isolation rules and SHALL include deterministic platform/run tests for interleaved requests, retry independence, and large-transfer non-starvation. - -#### Scenario: Contributor inspects channel docs -- **WHEN** a contributor opens run or platform protocol documentation -- **THEN** the docs MUST state that control and job lifecycle calls are lightweight, log ingest is durable and independently retried, artifact/file transfer is chunked and lower priority, and no lightweight route accepts heavy transfer payloads - -#### Scenario: Verification commands run -- **WHEN** the change is complete -- **THEN** `go test ./...` from `platform/`, `go test ./...` from `run/`, `scripts/check-structure.sh`, and `openspec validate harden-log-artifact-channel-isolation --strict` MUST pass diff --git a/openspec/changes/harden-log-artifact-channel-isolation/tasks.md b/openspec/changes/harden-log-artifact-channel-isolation/tasks.md deleted file mode 100644 index b9ce2e2..0000000 --- a/openspec/changes/harden-log-artifact-channel-isolation/tasks.md +++ /dev/null @@ -1,56 +0,0 @@ -## 1. Run-Side Channel Isolation - -- [x] 1.1 Add deterministic run tests that simulate slow or retrying artifact/file transfer work while control heartbeat and job ack/progress/result calls continue through bounded client calls. -- [x] 1.2 Add run tests proving log spool selection, upload acknowledgement, and retry cleanup continue while artifact/file chunks are queued, slow, or retrying. -- [x] 1.3 Add run tests proving artifact/file retry listing, chunk acknowledgement, and cleanup continue while log batches are queued, slow, or retrying. -- [x] 1.4 Update run scheduling, retry queue, or worker orchestration code only where needed to make the tests pass without exposing host paths, raw credentials, direct sockets, or large inline payloads through lightweight channels. -- [x] 1.5 Run `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` and record evidence. - -## 2. Platform Interleaving and Validation - -- [x] 2.1 Add platform service/API tests that interleave valid heartbeat, job ack/result, log batch ingest, and artifact transfer requests for one registered run endpoint. -- [x] 2.2 Prove each interleaved platform route mutates only its own state and preserves existing idempotency semantics. -- [x] 2.3 Add negative platform tests proving control, job, and log routes reject artifact chunks, file bodies, host paths, raw credentials, direct sockets, and channel-owned transport payloads from other routes. -- [x] 2.4 Update platform validators, DTOs, service logic, or route documentation only where needed to enforce the isolation contract. -- [x] 2.5 Run `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` and record evidence. - -## 3. Protocol Documentation - -- [x] 3.1 Update `run/protocol/*.md`, `run/spool/README.md`, `run/artifact/README.md`, `platform/protocol/run-contracts.md`, or `platform/api/routes.md` as needed to document channel priority and payload boundaries. -- [x] 3.2 Confirm docs state that control/job calls remain lightweight, log ingest is durable and independently retried, artifact/file transfer is chunked and lower priority, and lightweight routes never accept heavy transfer payloads. - -## 4. Verification and Stream Handoff - -- [x] 4.1 Record implementation evidence in this tasks file only after each command has actually run. -- [x] 4.2 Run `scripts/check-structure.sh` and record evidence. -- [x] 4.3 Run `openspec validate harden-log-artifact-channel-isolation --strict` and record evidence. -- [x] 4.4 Update `openspec/changes/architecture-delivery-stream/delivery-plan.md` to mark `harden-log-artifact-channel-isolation` complete only after evidence exists and move the next queue item to active. -- [x] 4.5 Update `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` with the next implementation/generator handoff after this change closes. - -## Evidence - -- Run-side channel isolation: - - Added `run/api/channel_isolation_test.go`, proving heartbeat, terminal job result, and log ingest complete while an artifact chunk upload is deliberately blocked. - - Added `run/spool/channel_isolation_test.go`, proving log acknowledgement cleanup remains independent from artifact backlog and artifact acknowledgement cleanup remains independent from log backlog. - - No run scheduling or queue production code changes were required; existing separate client calls and separate `logs` / `artifacts` spool areas satisfied the new regression tests. - - Initial sandbox run of `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./api ./spool -count=1` was blocked by `httptest` loopback bind permissions after `run/spool` passed. - - Escalated rerun of `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./api ./spool -count=1` passed for `browser.local/run/api` and `browser.local/run/spool`. - - Full sandbox run of `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` was blocked by `httptest` loopback bind permissions in `run/api` and `run/runtime`; non-listener packages passed. - - Escalated rerun of `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` passed for `api`, `config`, `protocol`, `runtime`, and `spool`. - -- Platform interleaving and validation: - - Added `platform/api/channel_isolation_handlers_test.go`, proving interleaved heartbeat, job ack/result, log batch ingest, and artifact transfer requests mutate only their own channel state. - - Added negative API coverage proving lightweight control/job/log routes reject artifact chunks, inline log arrays, host paths, raw credential fragments, direct socket strings, and heavy transfer payload fields through strict JSON decoding. - - Added rejection-state coverage proving a rejected heavy heartbeat payload does not mutate endpoint capacity or store heavy payload text. - - Focused command `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./api -run 'TestRunChannelAPI|TestLightweightRunRoutes' -count=1` passed. - - Full command `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` passed for `api`, `config`, `domain`, `dto`, `model`, `repo`, `service`, and `validator`. - -- Protocol documentation: - - Updated `run/spool/README.md`, `run/artifact/README.md`, `run/protocol/artifact.md`, `run/protocol/log-ingest.md`, and `run/protocol/job.md` with channel priority, independent retry, and heavy-payload boundary rules. - - Updated `platform/protocol/run-contracts.md` and `platform/api/routes.md` to state that control/job calls remain lightweight, log ingest is durable and independently retried, artifact/file transfer is lower priority and chunked, and lightweight routes reject heavy transfer payloads. - -- Final gates and stream handoff: - - `scripts/check-structure.sh` passed with `structure check passed`. - - `openspec validate harden-log-artifact-channel-isolation --strict` passed with `Change 'harden-log-artifact-channel-isolation' is valid`; the process exited 0. PostHog telemetry flush reported `ENOTFOUND edge.openspec.dev`, which did not affect validation. - - `openspec/changes/architecture-delivery-stream/delivery-plan.md` now marks `harden-log-artifact-channel-isolation` complete and `implement-local-debug-workspace` active. - - `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` now points the next generator chat at creating `implement-local-debug-workspace`, because that OpenSpec directory does not exist yet. diff --git a/openspec/changes/harden-platform-auth-and-secret-persistence/design.md b/openspec/changes/harden-platform-auth-and-secret-persistence/design.md deleted file mode 100644 index da71ca0..0000000 --- a/openspec/changes/harden-platform-auth-and-secret-persistence/design.md +++ /dev/null @@ -1,35 +0,0 @@ -# Design - -## Session records - -`AuthSessionRecord` stores only a SHA-256 token hash, user ID, issued/expiry timestamps, revoked timestamp, and rotation generation. The bearer token is returned once at login/rotation and is never stored in a snapshot. `GetCurrentUser` hashes the presented token and loads the record from the repository, rejecting missing, revoked, expired, or disabled-user sessions. The default TTL is bounded (8 hours); rotation revokes the prior generation before creating a new record. FileStore and MySQLStore snapshots load these records before serving requests. - -Strict production routers deliver the platform session in an `HttpOnly`, `SameSite=Strict` cookie and omit the token from JSON, so platform_web JavaScript does not persist new raw tokens. Explicit CLI/local tooling may request a bearer response with `X-Auth-Token-Response: bearer`; the explicitly named `NewTestRouterWithCore` compatibility constructor retains the bearer response contract for existing in-process tests. All normal router constructors enforce authorization. - -Run control state follows the same shape: a Run session has a bounded TTL, status, generation, capability fingerprint, and last-seen timestamp. Heartbeat/claim/ack/progress/result/cancel/reconcile/log/artifact operations must validate the current session and reject expired or revoked state. - -## Signed Run envelope - -HTTP Run channel requests carry `X-Run-Endpoint`, `X-Run-Timestamp`, `X-Run-Nonce`, and `X-Run-Signature`. The signature is HMAC-SHA256 over method, path, timestamp, nonce, and SHA-256 request body using the established Run session token as the channel key. Timestamps are accepted only inside a five-minute clock-skew window and each nonce is accepted once per Run session. The body is buffered and restored before JSON decoding. Legacy in-process service calls remain available for existing tests, but HTTP channel handlers reject missing/invalid signatures after session establishment. Signature errors are safe 401/403 failures and never echo token material. - -## Authorization matrix - -- Public: health, login, registration, and read-only marketplace/plugin discovery that contains no secret or host detail. -- Platform admin: user administration, AI provider metadata, plugin installation/registration/state, Run endpoint administration, platform metrics, audit inspection, and global job/artifact/log metadata. -- Authenticated server owner or assigned server administrator: server list/detail, runtime binding review, lifecycle, config diff/approval, logs, artifacts, distributions, dependency and client-manager actions for servers visible to that user. -- Owner or platform admin only: runtime binding writes, key reset, server administrator membership changes, destructive/archive actions. -- Run service identity: channelized control/jobs/logs/artifacts for its own endpoint and only resources addressed by a validated job/transfer/session; never browser bearer sessions. - -Services repeat ownership and role checks even when a route already checked them. Cross-owner reads/writes return `forbidden`; missing/invalid/expired credentials return `unauthorized`. - -## Secret boundary - -Secret-bearing writes accept only controlled `secret://` references or opaque logical refs already declared by a plugin. Stored records keep references, encrypted component-key material, status, generation, and fingerprints; raw values are never returned. DTOs expose only `configured`, `secret`, `presence`, and safe fingerprint/status fields. Snapshot tests scan serialized JSON and browser responses for secret literals, paths, sockets, and credentials. - -## Failure and migration behavior - -Older snapshots decode absent auth/session/secret arrays as empty. Existing users remain valid but must log in again after upgrade if no session record exists. Existing Run endpoints without a persisted session require a fresh signed hello. Expired/revoked records remain durable for audit and can be pruned in a later change. No rollback step writes raw secret data. - -## Deferred risks - -The controlled secret reference is not a production vault and encrypted component-key material still depends on the configured platform protection boundary. KMS/HSM integration, key wrapping, and multi-resource transactions are explicitly deferred and must not be represented as complete by this change. diff --git a/openspec/changes/harden-platform-auth-and-secret-persistence/proposal.md b/openspec/changes/harden-platform-auth-and-secret-persistence/proposal.md deleted file mode 100644 index 18500ae..0000000 --- a/openspec/changes/harden-platform-auth-and-secret-persistence/proposal.md +++ /dev/null @@ -1,33 +0,0 @@ -# Harden Platform Authentication, Authorization, and Secret Persistence - -## Why - -Platform account sessions and Run control sessions currently live only in process memory, so a restart invalidates valid clients and leaves revocation/expiry state implicit. Several management routes also rely on the caller reaching a UI path rather than enforcing an authenticated role or resource-ownership boundary at the API/service boundary. Existing component-key and distribution metadata is not included in the durable file/MySQL snapshot, while secret-bearing values must remain platform-owned and redacted. - -## What Changes - -- Persist hashed platform sessions and Run control session state with explicit issued/expiry/revoked timestamps and safe rotation. -- Require authenticated sessions for sensitive management APIs and enforce platform-admin, server-owner/administrator, and Run-service boundaries in handlers and services. -- Add a signed, timestamped Run request envelope for control/job/log/artifact channel requests where the HTTP boundary can validate a trusted Run session and reject stale/replayed messages. -- Persist existing encrypted component-key and distribution metadata in FileStore/MySQLStore snapshots, plus controlled secret metadata references and presence/fingerprint projections. -- Ensure auth failures are stable 401/403 API errors and the web client clears invalid sessions without rendering token, key, path, socket, or secret literals. -- Add regression coverage for login/reload, expiry/revocation/rotation, signed Run requests, cross-owner/role rejection, replay/clock failures, snapshot recovery, and non-disclosure. - -## Goals / Non-Goals - -**Goals:** - -- Make session and authorization decisions durable and independently enforceable from the UI. -- Keep raw credentials, host paths, direct sockets, and provider keys out of DTOs, logs, snapshots, and browser state. -- Preserve the independent `run` repository boundary and channel priorities. - -**Non-Goals:** - -- A production KMS/vault, encrypted secret-value storage, durable scheduling, process supervision, logs/artifacts backends, dependency installation, self-update, client-manager lifecycle, or production scaling. -- Re-adding Run source code to this repository or claiming the later roadmap is complete. - -## Impact - -- `platform/`: auth/session domain, repositories, snapshots, signed Run request validation, route authorization, safe secret metadata. -- `platform_web/`: API error/session handling and safe auth capability projections. -- `plugins/`: no raw credential or direct channel access; manifest/SDK contracts remain unchanged except for regression fixtures if needed. diff --git a/openspec/changes/harden-platform-auth-and-secret-persistence/specs/platform-auth-secrets/spec.md b/openspec/changes/harden-platform-auth-and-secret-persistence/specs/platform-auth-secrets/spec.md deleted file mode 100644 index 119f68c..0000000 --- a/openspec/changes/harden-platform-auth-and-secret-persistence/specs/platform-auth-secrets/spec.md +++ /dev/null @@ -1,71 +0,0 @@ -# Platform Authentication, Authorization, and Secret Persistence - -## ADDED Requirements - -### Requirement: Platform sessions are durable and bounded -The platform SHALL persist only hashed bearer-session records with user ownership, issued time, expiry time, revocation time, and rotation generation, and SHALL reject missing, expired, revoked, or disabled-user sessions. - -#### Scenario: Session survives restart -- **WHEN** a user logs in, the platform store is closed and reopened, and the same bearer token is presented before expiry -- **THEN** the platform restores the session record and authenticates the user without storing the raw token - -#### Scenario: Expired or revoked session is rejected -- **WHEN** an expired or revoked bearer token is presented -- **THEN** the API returns 401 and performs no protected read or write - -#### Scenario: Session rotation revokes the old generation -- **WHEN** an authenticated user rotates a session -- **THEN** a new token is issued, the previous generation is revoked durably, and the previous token is rejected - -#### Scenario: Browser session token is not script-readable -- **WHEN** a user logs in through the strict production router -- **THEN** the platform sets an HttpOnly SameSite session cookie and omits the raw token from the JSON response - -### Requirement: Run requests use a trusted bounded channel -Run control, job, log, and artifact channel requests SHALL validate the current endpoint session and, at the HTTP boundary, a timestamped HMAC signature with a five-minute clock-skew limit and single-use nonce. - -#### Scenario: Valid signed request -- **WHEN** a request is signed by the current Run session with an accepted timestamp and unused nonce -- **THEN** the request is processed for that endpoint and the nonce is recorded as used - -#### Scenario: Invalid, stale, or replayed request -- **WHEN** the signature is invalid, the timestamp is outside the skew window, or the nonce was already used -- **THEN** the request is rejected with a safe authentication error and no state mutation occurs - -### Requirement: Sensitive APIs enforce role and resource ownership -Sensitive user, provider, plugin, server, distribution, job, runtime-binding, audit, and channel metadata APIs SHALL enforce platform-admin, server-owner/administrator, or Run-service identity at the API and service boundaries. - -#### Scenario: Cross-owner access -- **WHEN** an authenticated non-owner requests another owner's server binding, job, config, artifact, or action -- **THEN** the platform returns 403 and leaves the resource unchanged - -#### Scenario: UI bypass -- **WHEN** a caller invokes a sensitive route directly without the required role or session -- **THEN** the platform rejects the call regardless of UI state or request shape - -### Requirement: Core auth and secret metadata are durable and redacted -FileStore and MySQLStore SHALL persist auth sessions, Run session state, encrypted component-key metadata, distributions, and controlled secret references through the repository snapshot contract; raw tokens, keys, credentials, paths, sockets, and provider secret values MUST NOT appear in snapshots, logs, DTOs, or browser responses. - -#### Scenario: Snapshot reload preserves safe metadata -- **WHEN** the store is reopened after creating a session, Run identity, component key, or secret reference -- **THEN** safe status/generation/presence metadata remains available and raw values remain absent - -#### Scenario: Secret presence projection -- **WHEN** a secret reference is configured -- **THEN** API and web responses expose only presence/configured/secret flags and safe fingerprints, never the reference value or storage location - -### Requirement: Web clients handle auth failures safely -The platform web client SHALL treat 401 as a session reset/re-login condition and 403 as a capability/ownership denial, without persisting or rendering raw tokens, credentials, paths, sockets, or secret references. - -#### Scenario: API session expires in the console -- **WHEN** an API call returns 401 -- **THEN** the client clears the bearer token and exposes a safe re-authentication state - -#### Scenario: API authorization is denied -- **WHEN** an API call returns 403 -- **THEN** the client reports a safe access-denied error without including secret or infrastructure details - -## Deferred Requirements - -- Production KMS/HSM/vault encryption and secret-value rotation are deferred. -- Durable scheduling, process supervision, logs/artifacts backends, dependency installation, self-update, client-manager lifecycle, and production scaling are outside this change. diff --git a/openspec/changes/harden-platform-auth-and-secret-persistence/tasks.md b/openspec/changes/harden-platform-auth-and-secret-persistence/tasks.md deleted file mode 100644 index 5759385..0000000 --- a/openspec/changes/harden-platform-auth-and-secret-persistence/tasks.md +++ /dev/null @@ -1,22 +0,0 @@ -## 1. Planning and contracts - -- [x] 1.1 Add typed durable auth-session and Run-session contracts, repository interfaces, validation rules, and safe DTO projections. -- [x] 1.2 Document the auth/authorization matrix, signed envelope, replay/clock constraints, secret-ref boundary, and deferred production risks. - -## 2. Durable authentication and Run trust - -- [x] 2.1 Persist hashed user sessions with expiry, revocation, rotation, and reload support in MemoryStore/FileStore/MySQLStore. -- [x] 2.2 Persist Run session state and enforce bounded lifecycle plus signed timestamp/nonce validation at HTTP channel boundaries. -- [x] 2.3 Add login/session-rotation/revocation routes and safe 401/403 error behavior. - -## 3. Authorization and secret boundary - -- [x] 3.1 Enforce platform-admin, owner/administrator, and Run-service authorization on sensitive routes and repeat checks in services. -- [x] 3.2 Persist component-key/distribution/secret metadata through all durable snapshots without raw secret disclosure. -- [x] 3.3 Add API and service regressions for cross-owner access, expired/revoked credentials, replay, and non-disclosure. - -## 4. Web and verification - -- [x] 4.1 Update platform_web API/session handling for 401/403 and capability-safe projections with no secret literals. -- [x] 4.2 Add frontend regressions for session reset/denied access and secret/path/socket non-disclosure. -- [x] 4.3 Run platform, plugin, web, OpenSpec strict validation, structure checks, and risk-relevant independent Run tests; record evidence and leave later roadmap work explicitly deferred. diff --git a/openspec/changes/implement-ai-provider-management/.openspec.yaml b/openspec/changes/implement-ai-provider-management/.openspec.yaml deleted file mode 100644 index 8e26fbe..0000000 --- a/openspec/changes/implement-ai-provider-management/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-02 diff --git a/openspec/changes/implement-ai-provider-management/design.md b/openspec/changes/implement-ai-provider-management/design.md deleted file mode 100644 index 1e5b5d6..0000000 --- a/openspec/changes/implement-ai-provider-management/design.md +++ /dev/null @@ -1,77 +0,0 @@ -## Context - -The platform already has domain, DTO, validator, repository, service, and HTTP API foundations for AI provider resources. The route catalog previously deferred provider test/model actions, and `platform_web/pages/AiProvidersPage.tsx` is still a placeholder. The architecture requires AI provider credentials and base URLs to remain platform-owned, and plugin pages must never receive raw provider keys. - -This change turns AI provider management into a usable first-party workflow across `platform/` and `platform_web/` while keeping the scope intentionally local: configuration validation, status management, and model inventory are platform metadata operations, not live external model calls. - -## Goals / Non-Goals - -**Goals:** - -- Add backend AI provider management APIs for update, enable/disable, configuration test, and model listing. -- Keep all provider responses redacted to `apiKeyRef`; reject raw keys in create and update paths. -- Keep management behavior inside `service.Core` and named DTOs, with handlers acting as transport adapters. -- Implement a functional AI provider management page in `platform_web` with API client/types, create/edit form, status filters, model display, enable/disable, and test actions. -- Add backend and frontend tests for management behavior and secret redaction. - -**Non-Goals:** - -- No real OpenAI/Claude/local provider network calls. -- No secret vault implementation or raw secret storage. -- No plugin-facing AI invocation API. -- No AI-generated config diff/write dispatch. -- No authentication, RBAC, SQL persistence, run-side behavior, billing, cloud host sales, or agent-provider/cloud-provider workflows. - -## Decisions - -### Decision 1: Provider test is metadata validation - -The test endpoint will validate stored provider metadata and report whether the provider is active, has a secret reference when required, includes a default model in its model list, and passes existing validator rules. It will not contact external AI services. - -Alternative considered: performing a live chat/model request. Rejected because this change must not introduce external network behavior, raw key handling, or provider-specific clients. - -### Decision 2: Status changes use a dedicated action route - -Enable/disable behavior will use `POST /api/v1/ai-providers/{id}/status` with a named status request DTO. General update will edit provider metadata while preserving status unless the dedicated action changes it. - -Alternative considered: overloading generic update with status changes. Rejected because explicit status actions are easier to audit and test. - -### Decision 3: Update uses full provider metadata - -The update request will accept the same safe fields as create plus provider metadata fields, with no raw key field. `apiKeyRef` remains a secret reference string and is validated the same way as create. - -Alternative considered: partial patch semantics. Rejected for this stage because full update is deterministic, simpler to validate, and matches the existing in-memory repository implementation. - -### Decision 4: Frontend page owns UI state but not contracts - -`AiProvidersPage` will manage local loading/form selection state, while API DTOs and client functions remain in `platform_web/api`. The page will use API responses for persisted provider data and seed a local demo fallback only when the backend is unavailable in standalone frontend development. - -Alternative considered: hard-coded page data only. Rejected because this would not exercise the platform API client or management workflow. - -### Decision 5: UI stays operational and dense - -The AI provider page will use a table, compact metrics, a form panel, filter controls, and action buttons. It will avoid marketing layout and will not display instructional copy or raw secrets. - -Alternative considered: a large hero/empty-state page. Rejected because this is an operational console area used for repeated configuration work. - -## Risks / Trade-offs - -- [Risk] The test endpoint can only validate metadata, not live connectivity. Mitigation: return an explicit `mode` value and reserve live tests for a later provider invocation change. -- [Risk] Frontend fallback data could be mistaken for persisted data. Mitigation: mark fallback state as local-only in view state and prefer API data whenever the backend responds. -- [Risk] Full update requires clients to send all editable provider fields. Mitigation: centralize the request builder in the page and API client. -- [Risk] In-memory backend state remains process-local. Mitigation: retain service/router injection and leave persistence to a future storage change. - -## Migration Plan - -1. Add backend DTOs, service methods, handler routes, and route catalog updates for AI provider management. -2. Add backend service/API tests covering update, status, test/model responses, duplicate/missing resources, and raw key rejection. -3. Add frontend API types/client methods, replace the placeholder AI provider page, and add rendering/client tests. -4. Run backend tests, frontend tests/build, structure check, browser walkthrough, and strict OpenSpec validation. - -Rollback before dependent changes is removal of the new AI provider management endpoints/page and this OpenSpec change. After dependent plugin or frontend workflows consume these APIs, rollback must be handled through a new OpenSpec change. - -## Open Questions - -- Which persistence-backed secret reference provider should store `apiKeyRef` targets? -- Which later change should add live provider connectivity tests and model discovery calls? -- Which authorization policy will restrict who can create or disable providers? diff --git a/openspec/changes/implement-ai-provider-management/proposal.md b/openspec/changes/implement-ai-provider-management/proposal.md deleted file mode 100644 index 0ddbfab..0000000 --- a/openspec/changes/implement-ai-provider-management/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -AI providers are a required first-party platform area, but the backend and console currently expose only the generic core resource API and a placeholder page. Operators need a usable management workflow that configures model endpoints safely without exposing raw provider credentials to plugins or UI responses. - -## What Changes - -- Add AI-provider-specific backend management actions for update, enable/disable, configuration test, and configured model listing. -- Preserve the existing create/list/detail API while tightening response behavior around secret references and raw key rejection. -- Add service methods and DTOs for AI provider management without adding external provider calls or raw secret storage. -- Replace the `platform_web` placeholder AI provider page with a functional management view that lists providers, creates/edits provider metadata, toggles status, tests configuration, and displays model inventory. -- Add frontend API types/client methods and tests that assert raw keys are never part of returned provider shapes. - -## Capabilities - -### New Capabilities - -- `ai-provider-management`: Safe platform and management-console workflows for creating, editing, enabling/disabling, testing, and viewing AI provider configuration. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` and `platform_web/` only. -- Extends AI provider DTOs, service methods, API handlers, route catalog, frontend API contracts, and the AI provider page. -- Adds backend API tests, frontend rendering/client tests, and a browser walkthrough. -- Does not add raw key exposure, plugin-facing raw credentials, run-side behavior, external AI network invocation, billing, cloud host sales, or agent-provider/cloud-provider workflows. diff --git a/openspec/changes/implement-ai-provider-management/specs/ai-provider-management/spec.md b/openspec/changes/implement-ai-provider-management/specs/ai-provider-management/spec.md deleted file mode 100644 index 0019c3b..0000000 --- a/openspec/changes/implement-ai-provider-management/specs/ai-provider-management/spec.md +++ /dev/null @@ -1,79 +0,0 @@ -## ADDED Requirements - -### Requirement: AI providers can be managed through platform APIs -The platform SHALL expose AI provider management APIs for create, list, detail, update, enable/disable, configuration test, and configured model listing. - -#### Scenario: Provider is updated -- **WHEN** a client sends a valid provider update request to an existing AI provider -- **THEN** the platform MUST validate the request, persist the metadata through `service.Core`, and return a redacted `AIProviderResponse` - -#### Scenario: Provider status is changed -- **WHEN** a client enables or disables an existing AI provider through the status action route -- **THEN** the platform MUST persist the requested status and return a redacted `AIProviderResponse` - -#### Scenario: Provider configuration is tested -- **WHEN** a client tests an existing AI provider -- **THEN** the platform MUST validate stored metadata locally and return a named test result DTO without contacting external AI services - -#### Scenario: Provider model list is requested -- **WHEN** a client requests configured models for an existing AI provider -- **THEN** the platform MUST return the provider ID, default model, and configured model names without exposing credentials - -### Requirement: AI provider management preserves secret boundaries -AI provider management SHALL reject raw key material in request fields and SHALL never expose raw API keys in API responses or frontend-visible types. - -#### Scenario: Raw key is submitted during update -- **WHEN** a create or update request includes raw key material instead of a secret reference in `apiKeyRef` -- **THEN** the platform MUST reject the request with a validation error and MUST NOT persist the provider - -#### Scenario: Provider is returned to UI -- **WHEN** the backend or frontend API client returns provider data -- **THEN** the response/type MUST include `apiKeyRef` only and MUST NOT include `apiKey`, `rawApiKey`, or equivalent raw credential fields - -### Requirement: AI provider service owns management invariants -The platform service layer SHALL own AI provider update, status, local test, and model-list behavior rather than implementing those rules directly in HTTP handlers. - -#### Scenario: Management handler receives request -- **WHEN** an AI provider management HTTP handler accepts a request -- **THEN** it MUST decode named DTOs, call `service.Core`, and encode named DTO responses - -#### Scenario: Missing provider is managed -- **WHEN** a management action targets a missing provider ID -- **THEN** the platform MUST return a stable `404` JSON error response - -### Requirement: AI provider console page is functional -The management console SHALL replace the placeholder AI provider page with a functional operational view for configured providers. - -#### Scenario: Operator opens AI provider page -- **WHEN** the AI provider page renders -- **THEN** it MUST show provider counts, status distribution, configured model counts, and a provider table - -#### Scenario: Operator edits provider form -- **WHEN** an operator creates or edits a provider through the page form -- **THEN** the page MUST submit named API requests and refresh or update the provider list without displaying raw key material - -#### Scenario: Operator uses provider actions -- **WHEN** an operator triggers enable/disable, test, or model-list actions -- **THEN** the page MUST call the matching API client methods and display the redacted result state - -### Requirement: Frontend contracts are centralized -The frontend SHALL keep AI provider API types and client methods in `platform_web/api` and SHALL keep shared UI contracts out of page-local hidden types. - -#### Scenario: Page consumes provider data -- **WHEN** `AiProvidersPage` needs provider data or actions -- **THEN** it MUST use named API types and `PlatformApiClient` methods instead of inline fetch contracts - -#### Scenario: Frontend tests inspect provider types -- **WHEN** frontend tests check provider response shapes -- **THEN** they MUST confirm raw key fields are absent from returned provider data - -### Requirement: AI provider management is verified end to end -The change SHALL include backend API/service tests, frontend tests/build, a browser walkthrough, structure validation, and strict OpenSpec validation. - -#### Scenario: Verification commands run -- **WHEN** the change is complete -- **THEN** `go test ./...` from `platform/`, frontend tests/build, `scripts/check-structure.sh`, and `openspec validate implement-ai-provider-management --strict` MUST pass - -#### Scenario: Browser walkthrough runs -- **WHEN** frontend AI provider page behavior is claimed complete -- **THEN** a browser walkthrough MUST verify the page renders, exposes the AI provider workflow, and does not show raw credential fields diff --git a/openspec/changes/implement-ai-provider-management/tasks.md b/openspec/changes/implement-ai-provider-management/tasks.md deleted file mode 100644 index e540877..0000000 --- a/openspec/changes/implement-ai-provider-management/tasks.md +++ /dev/null @@ -1,36 +0,0 @@ -## 1. Backend Contracts And Service - -- [x] 1.1 Add AI provider update, status, test, and model-list DTO contracts with redacted response shapes. -- [x] 1.2 Extend `service.Core` with AI provider update, status, local test, and model-list methods using existing validators and repositories. - -## 2. Backend API Surface - -- [x] 2.1 Implement AI provider management routes for update, status, test, and models using named DTOs and service methods. -- [x] 2.2 Update platform route/protocol documentation for implemented AI provider management routes and deferred live invocation. -- [x] 2.3 Add backend service/API tests for update, enable/disable, test/models, missing resources, duplicate handling, and raw key rejection. - -## 3. Frontend Contracts And Page - -- [x] 3.1 Add centralized `platform_web/api` AI provider types and `PlatformApiClient` methods for list/create/update/status/test/models. -- [x] 3.2 Replace the placeholder AI provider page with a functional operational management view using the API client and no raw key display. -- [x] 3.3 Add frontend tests for page rendering, management actions, API client calls, and raw-key field absence. - -## 4. Verification - -- [x] 4.1 Run `go test ./...` from `platform/` and record evidence. -- [x] 4.2 Run frontend tests/build from `platform_web/` and record evidence. -- [x] 4.3 Run a browser walkthrough of the AI provider page and record evidence. -- [x] 4.4 Run `scripts/check-structure.sh` and record evidence. -- [x] 4.5 Run `openspec validate implement-ai-provider-management --strict` and record evidence. - -## Evidence - -- `go test ./domain ./dto`: passed. -- `go test ./service ./api`: passed. -- `go test ./...` from `platform/`: passed. -- `npm test` from `platform_web/`: passed. -- `npm run typecheck` from `platform_web/`: passed. -- `npm run build` from `platform_web/`: passed. -- Browser walkthrough with Playwright Chromium against `http://127.0.0.1:5173/#/aiProviders`: passed; rendered AI provider management, created `Browser Check Provider`, tested metadata, toggled status, and verified visible text did not contain `rawApiKey`, `api_key=`, `Bearer `, or `sk-`. -- `scripts/check-structure.sh`: passed. -- `openspec validate implement-ai-provider-management --strict`: passed. diff --git a/openspec/changes/implement-artifact-download-and-browser-transfer/.openspec.yaml b/openspec/changes/implement-artifact-download-and-browser-transfer/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-artifact-download-and-browser-transfer/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-artifact-download-and-browser-transfer/design.md b/openspec/changes/implement-artifact-download-and-browser-transfer/design.md deleted file mode 100644 index 1f63754..0000000 --- a/openspec/changes/implement-artifact-download-and-browser-transfer/design.md +++ /dev/null @@ -1,61 +0,0 @@ -## Context - -The existing artifact transfer channel handles run-to-platform upload with chunk/resume semantics and platform-owned artifact metadata. Browser consumers need the opposite user-facing surface: list and download available artifacts from server/job/plugin contexts through platform authorization. The browser should receive safe references and platform routes, not raw storage locations. - -## Goals / Non-Goals - -**Goals:** - -- Add platform artifact download metadata and content routes for authorized browser users and plugin bridge actions. -- Support bounded chunk/range reads or download responses that can report progress in the frontend. -- Enforce artifact owner scope, user/server access, plugin permissions, availability state, and response redaction. -- Add frontend API client methods and UI controls for artifact download/open from operational pages. -- Add plugin bridge artifact helpers that return safe references rather than raw paths or storage credentials. -- Verify with tests and browser walkthrough. - -**Non-Goals:** - -- No external object storage backend or presigned raw storage URLs. -- No browser direct access to run endpoints, host filesystem paths, sockets, or storage backend credentials. -- No artifact upload from browser unless a future change explicitly adds it. -- No archive extraction, malware scanning, lifecycle cleanup, billing, cloud host sales, or unrelated marketplace behavior. - -## Decisions - -### Decision 1: Browser downloads go through platform routes - -The platform exposes artifact content through authorized API routes. Any download URL or token is a platform route scoped to the requesting user/session and artifact, not a raw backend location. - -Alternative considered: return storage adapter paths or presigned backend URLs. Rejected because no storage backend exists yet and raw locations can leak internals. - -### Decision 2: Artifact availability is required for download - -Only artifacts in an available/complete state can be downloaded by browser consumers. Uploading, failed, missing, or unauthorized artifacts return stable safe errors. - -Alternative considered: stream partial uploading artifacts. Rejected because partial reads complicate integrity and user expectations. - -### Decision 3: Plugin bridge receives artifact references, not bytes by default - -Bridge actions can request artifact metadata/open/download references. Large byte transfer stays in platform/browser client code, preserving bounded payloads across plugin bridge messages. - -Alternative considered: pass base64 artifact bytes through plugin page bridge messages. Rejected because large payloads can block UI and violate channel separation. - -## Risks / Trade-offs - -- [Risk] In-memory artifact payload storage limits realistic download size. Mitigation: keep interfaces ready for storage adapters and test bounded content behavior. -- [Risk] Browser downloads can expose sensitive server files if ownership checks are weak. Mitigation: validate artifact owner scope, user/server access, plugin permissions, and availability before content reads. -- [Risk] Plugin pages may expect direct bytes. Mitigation: provide safe artifact references and frontend host download helpers. - -## Migration Plan - -1. Add platform artifact download contracts, validators, service methods, routes, and docs. -2. Add frontend API client, UI controls, bridge host helpers, and tests. -3. Add plugin SDK artifact reference helpers/tests if needed. -4. Run browser walkthrough, structure check, and strict OpenSpec validation. - -Rollback removes browser download routes/client integration and this change's artifacts before plugin pages depend on them. - -## Open Questions - -- Which durable artifact storage adapter should back downloads after in-memory transfer state is replaced? -- Whether browser upload should be a separate future transfer direction. diff --git a/openspec/changes/implement-artifact-download-and-browser-transfer/proposal.md b/openspec/changes/implement-artifact-download-and-browser-transfer/proposal.md deleted file mode 100644 index 4dc8ad8..0000000 --- a/openspec/changes/implement-artifact-download-and-browser-transfer/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -Run can upload artifacts to the platform transfer channel, but operators and plugin pages still need a safe way to discover, download, and hand off artifact references in the browser. Artifact download must remain platform-mediated so browser code never receives raw storage backend credentials, raw host paths, or direct run sockets. - -## What Changes - -- Add platform artifact download/read APIs that authorize artifact access and stream or return bounded content through platform-owned routes. -- Add browser-facing artifact metadata, download URL/token, chunk read, and transfer progress contracts without exposing storage internals. -- Add frontend API client and UI behavior for artifact download from server/job/plugin contexts. -- Integrate plugin bridge artifact actions with browser-safe artifact references. -- Add platform and frontend tests plus browser walkthrough for download, access denial, resume/progress, and no-secret rendering. - -## Capabilities - -### New Capabilities - -- `artifact-download-and-browser-transfer`: Browser-safe platform-mediated artifact discovery, download, and plugin bridge transfer references. - -### Modified Capabilities - -- Builds on `artifact-transfer-channel`, plugin bridge contracts, and server/job workflows without changing run upload semantics. - -## Impact - -- Affects `platform/` artifact DTOs, validators, services, APIs, and docs. -- Affects `platform_web/` API contracts, artifact UI/download behavior, plugin bridge host integration, and tests. -- May affect `plugins/` SDK artifact reference helpers/tests. -- Does not add external object storage, presigned raw backend URLs, direct plugin-to-run access, billing, cloud host sales, or unrelated SaaS marketplace features. diff --git a/openspec/changes/implement-artifact-download-and-browser-transfer/specs/artifact-download-and-browser-transfer/spec.md b/openspec/changes/implement-artifact-download-and-browser-transfer/specs/artifact-download-and-browser-transfer/spec.md deleted file mode 100644 index cdd0d3f..0000000 --- a/openspec/changes/implement-artifact-download-and-browser-transfer/specs/artifact-download-and-browser-transfer/spec.md +++ /dev/null @@ -1,57 +0,0 @@ -## ADDED Requirements - -### Requirement: Browser artifact downloads are platform-mediated - -The platform SHALL provide browser-safe artifact metadata and content download APIs that authorize access and do not expose storage backend credentials, raw host paths, or direct run sockets. - -#### Scenario: Authorized artifact download starts -- **WHEN** an authorized operator requests download metadata for an available artifact in an accessible server or job context -- **THEN** the platform MUST return a browser-safe artifact reference or platform download route with filename, content type, size, checksum, and expiry metadata - -#### Scenario: Unauthorized artifact download is denied -- **WHEN** a user or plugin page requests an artifact outside its server, job, or plugin permission scope -- **THEN** the platform MUST return a stable safe error and MUST NOT return artifact bytes or download references - -### Requirement: Artifact content reads are bounded and integrity-aware - -The platform SHALL validate artifact availability, requested range/chunk bounds, checksum metadata, and response size before returning artifact content to browser clients. - -#### Scenario: Available artifact content is read -- **WHEN** a browser client requests a valid byte range or full download for an available artifact -- **THEN** the platform MUST return content with safe headers and integrity metadata - -#### Scenario: Incomplete artifact cannot be downloaded -- **WHEN** a browser client requests an uploading, failed, missing, or incomplete artifact -- **THEN** the platform MUST reject the request and leave artifact state unchanged - -### Requirement: Frontend exposes artifact download workflow - -The frontend SHALL provide centralized API client methods and UI controls for artifact download/open flows from server, job, or plugin contexts. - -#### Scenario: Operator downloads artifact -- **WHEN** an operator clicks an artifact download/open action -- **THEN** the page MUST request platform download metadata/content, show progress or completion state, and avoid raw path/credential display - -#### Scenario: Download fails safely -- **WHEN** an artifact download request fails validation, authorization, or availability checks -- **THEN** the UI MUST show a safe error state without exposing backend paths, run sockets, storage credentials, or raw secrets - -### Requirement: Plugin bridge uses artifact references safely - -The plugin bridge SHALL expose artifact actions as safe metadata or download references rather than raw bytes, host paths, direct run endpoints, or storage backend credentials. - -#### Scenario: Plugin page opens artifact reference -- **WHEN** a plugin page requests an allowed artifact action -- **THEN** the platform/host MUST return a scoped artifact reference that the browser host can download through platform APIs - -#### Scenario: Plugin page lacks artifact permission -- **WHEN** a plugin page requests artifact access without required manifest/page permission -- **THEN** the platform MUST deny the request before returning metadata, bytes, or download references - -### Requirement: Artifact download is verified end to end - -The change SHALL include backend tests, frontend tests/build, plugin SDK tests if artifact bridge helpers are added, browser walkthrough evidence, structure validation, and strict OpenSpec validation. - -#### Scenario: Verification commands pass -- **WHEN** the change is complete -- **THEN** platform tests, platform_web tests/typecheck/build, relevant plugin tests, `scripts/check-structure.sh`, and `openspec validate implement-artifact-download-and-browser-transfer --strict` MUST pass diff --git a/openspec/changes/implement-artifact-download-and-browser-transfer/tasks.md b/openspec/changes/implement-artifact-download-and-browser-transfer/tasks.md deleted file mode 100644 index 6fa9e54..0000000 --- a/openspec/changes/implement-artifact-download-and-browser-transfer/tasks.md +++ /dev/null @@ -1,46 +0,0 @@ -## 1. Platform Artifact Download Contracts - -- [x] 1.1 Add domain and DTO contracts for browser artifact metadata, download references, range/content requests, progress, and safe errors. -- [x] 1.2 Add validators for artifact IDs, owner/user/plugin scope, availability state, range bounds, response size, checksum metadata, and unsafe secret/path/socket content. -- [x] 1.3 Add service methods for authorized artifact metadata lookup, download reference creation, and bounded content reads. - -## 2. Platform Artifact Download API - -- [x] 2.1 Implement artifact metadata/download reference route using named DTOs and service methods. -- [x] 2.2 Implement bounded artifact content/range route with safe headers and integrity metadata. -- [x] 2.3 Integrate plugin bridge artifact actions with safe artifact references. -- [x] 2.4 Update platform route/protocol documentation for browser artifact download and deferred storage backend behavior. -- [x] 2.5 Add platform tests for successful download, range reads, unavailable artifacts, unauthorized scope, unsafe references, and no raw path/credential responses. - -## 3. Frontend Browser Transfer - -- [x] 3.1 Add centralized `platform_web/api` artifact download types and client methods. -- [x] 3.2 Add UI controls/state for artifact download/open flows from relevant server/job/plugin contexts. -- [x] 3.3 Add bridge host handling for artifact references and browser-mediated download actions. -- [x] 3.4 Add frontend tests for progress/success/error states, unauthorized failures, and no raw secret/path rendering. - -## 4. Plugin SDK Artifact Helpers - -- [x] 4.1 Add or update plugin SDK helpers for artifact bridge request/reference parsing if bridge artifact actions need new helper types. -- [x] 4.2 Add plugin tests for artifact reference helpers and forbidden direct run/storage access assumptions if helper code changes. - -## 5. Verification - -- [x] 5.1 Run `cd platform && go test ./...` and record evidence. -- [x] 5.2 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 5.3 Run relevant plugin tests/typecheck if plugin SDK helpers changed and record evidence. -- [x] 5.4 Run browser walkthrough for artifact download/browser transfer and record evidence. -- [x] 5.5 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.6 Run `openspec validate implement-artifact-download-and-browser-transfer --strict` and record evidence. - -## Evidence - -- 1.1-2.5: `cd platform && go test ./api -run TestArtifactDownload` passed, covering browser-safe references, bounded content/range reads, unavailable artifact rejection, unauthorized scope denial, bridge `artifacts.open`, and forbidden fragment checks. -- 3.1-3.4: `cd platform_web && npm run typecheck` passed. `cd platform_web && npm test -- --run api/client.test.ts utils/pluginBridgeHost.test.ts pages/ServerDetailPage.test.tsx` passed, covering artifact client methods, chunk metadata, bridge artifact reference parsing/rejection, and server detail artifact workflow source checks. -- 4.1-4.2: `cd plugins && npm run typecheck` passed. `cd plugins && npm test` passed, covering `createArtifactOpenRequest`, `parseArtifactReference`, permission checks, and rejection of direct storage URL assumptions. -- 5.1: `cd platform && go test ./...` passed. -- 5.2: `cd platform_web && npm run typecheck` passed; `cd platform_web && npm test` passed; `cd platform_web && npm run build` passed. -- 5.3: `cd plugins && npm run typecheck` passed; `cd plugins && npm test` passed. -- 5.4: Browser walkthrough passed with a temporary local mock server and headless Chrome: opened server detail, selected `操作历史`, clicked artifact `打开`, observed `已打开 artifact-walk.bin`, and checked rendered text for forbidden path/token/storage fragments. Temporary walkthrough files were removed. -- 5.5: `scripts/check-structure.sh` passed. -- 5.6: `openspec validate implement-artifact-download-and-browser-transfer --strict` reported `Change 'implement-artifact-download-and-browser-transfer' is valid`. PostHog telemetry flush logged DNS errors afterward, but validation exited 0. diff --git a/openspec/changes/implement-artifact-transfer-channel/.openspec.yaml b/openspec/changes/implement-artifact-transfer-channel/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-artifact-transfer-channel/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-artifact-transfer-channel/design.md b/openspec/changes/implement-artifact-transfer-channel/design.md deleted file mode 100644 index ae72979..0000000 --- a/openspec/changes/implement-artifact-transfer-channel/design.md +++ /dev/null @@ -1,86 +0,0 @@ -## Context - -Run control, job lifecycle, and durable log ingest are implemented as separate HTTP JSON channels. Artifact metadata exists in the platform, and job results can reference artifacts, but there is no transfer workflow that can move large run-produced files into platform-managed artifact records with resume and checksum semantics. - -This change implements the first run-to-platform artifact upload channel. Platform storage remains in memory and artifact payloads are held only long enough to prove chunk ordering and final checksum behavior. The channel is intentionally separate from control, jobs, logs, and the optional game client bridge so large payloads do not share those routes or DTOs. - -## Goals / Non-Goals - -**Goals:** - -- Define typed artifact transfer payloads in `run/protocol` and matching platform DTO/domain contracts. -- Add platform artifact transfer routes for open, chunk upload, resume/status, and complete. -- Validate active run session, owner relationship, transfer identity, bounded chunk size, chunk checksum, byte ranges, resume state, and final checksum. -- Update existing `Artifact` metadata from `uploading` to `available` only after every chunk is present and the final checksum matches. -- Add a run-side artifact spool/queue that persists unacknowledged chunk upload requests and deletes them only after platform acknowledgement. -- Extend `run/api.PlatformClient` with typed artifact transfer methods. -- Add tests for platform service/API transfer behavior, resume, duplicate chunk acknowledgement, checksum errors, completion errors, run spool retention, and client request/response handling. - -**Non-Goals:** - -- No platform-to-run download flow, browser upload/download UI, external object storage backend, presigned URL flow, or streaming transport. -- No plugin bridge file APIs, AI artifact inspection, archive extraction, or artifact lifecycle cleanup jobs. -- No raw host paths, raw credentials, direct sockets, logs, or job result bodies inside artifact chunk requests. -- No billing, cloud host sales, agent-provider/cloud-provider workflows, or direct plugin-to-run access. - -## Decisions - -### Decision 1: HTTP JSON chunk endpoints first - -The initial channel uses separate JSON `POST` endpoints under `/api/v1/run/artifacts/*`: `open`, `chunks`, `status`, and `complete`. Chunk payloads use JSON byte encoding, which Go represents as base64, and validators enforce a bounded maximum chunk size. - -Alternative considered: multipart upload or object-storage signed URLs. Rejected for this change because there is no storage backend yet, and the first requirement is to prove protocol, validation, resume, and checksum semantics in tests. - -### Decision 2: Run uploads only in this change - -The transfer direction is explicit but only `upload` is accepted. Platform-to-run download will need separate authorization, cache, and throttling semantics after upload behavior is stable. - -Alternative considered: implementing upload and download together. Rejected because download would add browser/plugin access questions and storage-adapter behavior that are outside this queue item. - -### Decision 3: Existing Artifact metadata remains the public resource - -Opening a transfer creates or validates the existing `Artifact` metadata record in `uploading` state. Completion updates that same record to `available`; failed checksum or missing chunk errors leave the artifact non-available. - -Alternative considered: adding a separate persisted transfer model now. Rejected because current platform persistence is in-memory and the transfer session can stay behind `service.Core` until a database-backed storage change exists. - -### Decision 4: Chunks are accepted idempotently by checksum - -The platform records received chunk indexes, byte ranges, sizes, checksums, and payload bytes in memory. Re-uploading the same chunk with the same checksum returns a duplicate acknowledgement; re-uploading a different payload for an acknowledged index is rejected. - -Alternative considered: allowing overwrite of existing chunk indexes. Rejected because resumable upload cleanup must be deterministic and conflicting retries should be visible immediately. - -### Decision 5: Owner authorization is platform mediated - -Run uploads are accepted only for job-owned or server-instance-owned artifacts that belong to the requesting run endpoint. Platform/plugin-owned artifact records can still be created through metadata APIs, but this run transfer channel does not let a run endpoint spoof unrelated owners. - -Alternative considered: accepting any artifact owner kind. Rejected because run must not become a direct write path for platform/plugin-owned data without an explicit authorization change. - -### Decision 6: Run spool stores chunk upload requests, not host paths - -The run-side artifact spool writes one JSON file per pending chunk request. It stores the bounded request payload needed for retry and never stores or exposes the local host path that originally produced the bytes. - -Alternative considered: storing file path plus offset for retry. Rejected because run must enforce scoped paths and must not expose raw host paths through platform-facing transfer state. - -## Risks / Trade-offs - -- [Risk] In-memory platform chunk storage disappears on restart. Mitigation: keep transfer state behind `service.Core`; storage adapters and durable transfer sessions can replace it later. -- [Risk] JSON/base64 chunks are inefficient for large production artifacts. Mitigation: enforce bounded chunks now and leave streaming/object-storage transfer to a later change. -- [Risk] No background artifact uploader exists. Mitigation: run client and spool semantics are implemented and tested; scheduling and priority throttling can build on them later. -- [Risk] Upload-only support does not cover all artifact use cases. Mitigation: explicitly keep direction in the protocol so a future download change can extend without renaming the channel. - -## Migration Plan - -1. Add artifact transfer protocol, DTO, domain, validation, and service contracts. -2. Add platform API handlers and tests for open, chunk upload, resume/status, and complete. -3. Add run artifact spool implementation and tests. -4. Add run client methods and tests. -5. Update protocol and route docs. -6. Verify with platform tests, run tests, structure check, and strict OpenSpec validation. - -Rollback before dependent changes is removal of the artifact transfer route/client/spool additions and this OpenSpec change. After server workflows depend on artifact transfer, rollback must use a new OpenSpec change. - -## Open Questions - -- Which durable artifact storage backend should be implemented first: local segments, filesystem package storage, S3-compatible object storage, or another adapter? -- What production chunk size, concurrency limits, and backoff policy should artifact uploaders use? -- How should platform-to-run download authorization interact with plugin pages and server management workflows? diff --git a/openspec/changes/implement-artifact-transfer-channel/proposal.md b/openspec/changes/implement-artifact-transfer-channel/proposal.md deleted file mode 100644 index 1c66b78..0000000 --- a/openspec/changes/implement-artifact-transfer-channel/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -Jobs and logs now have separate run-platform channels, but large files still only exist as artifact metadata or opaque result references. This change adds the first artifact transfer channel so run can upload and resume bounded file chunks with checksum verification without blocking control, job, or log traffic. - -## What Changes - -- Add typed run artifact transfer protocol payloads for transfer creation, chunk upload, resume status, and completion acknowledgements. -- Add platform API routes that create artifact transfer sessions, accept bounded chunks, validate sequence/order/checksums, report resume state, and complete verified artifacts. -- Extend platform service behavior to store chunk state in memory, update existing artifact metadata, and keep artifact transfer traffic separate from control, job, and log workflows. -- Add a run-side local artifact transfer queue/spool abstraction that records pending chunk manifests and removes chunks only after platform acknowledgement. -- Extend the run-side platform client with typed artifact transfer methods. -- Add platform service/API tests and run queue/client tests covering chunk resume, checksum failures, completion validation, retry cleanup, and channel isolation assumptions. - -## Capabilities - -### New Capabilities - -- `artifact-transfer-channel`: Chunked and resumable run-to-platform artifact transfer, checksum validation, local retry retention, and transfer completion workflow. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` and `run/` only. -- Adds Go protocol/DTO/domain/service/API/queue code and tests for artifact transfer. -- Updates run/platform protocol and route documentation. -- Does not implement browser upload/download UI, external object storage backends, plugin bridge file access, AI artifact inspection, billing, cloud host sales, or direct plugin/run access. diff --git a/openspec/changes/implement-artifact-transfer-channel/specs/artifact-transfer-channel/spec.md b/openspec/changes/implement-artifact-transfer-channel/specs/artifact-transfer-channel/spec.md deleted file mode 100644 index ed776ac..0000000 --- a/openspec/changes/implement-artifact-transfer-channel/specs/artifact-transfer-channel/spec.md +++ /dev/null @@ -1,65 +0,0 @@ -## ADDED Requirements - -### Requirement: Artifact transfer channel is separate from other run channels - -The platform SHALL expose artifact transfer behavior through dedicated run artifact routes and SHALL NOT require control, job, or log routes to carry artifact chunk payloads. - -#### Scenario: Dedicated artifact routes handle chunks -- **WHEN** a registered run endpoint uploads an artifact chunk -- **THEN** the request is handled by a run artifact transfer route and no control, job, or log route accepts the chunk payload - -### Requirement: Run opens upload transfer sessions - -The platform SHALL allow an active run session to open an upload transfer for a job-owned or server-instance-owned artifact assigned to that run endpoint. - -#### Scenario: Valid upload session opens -- **WHEN** a run endpoint opens an upload transfer with a valid session token, artifact metadata, owner, total size, chunk size, checksum, and idempotency key -- **THEN** the platform returns an accepted transfer ID, records the artifact in uploading state, and reports no received chunks - -#### Scenario: Invalid owner is rejected -- **WHEN** a run endpoint opens an upload transfer for an artifact owner that is not assigned to that run endpoint -- **THEN** the platform rejects the request without marking the artifact available - -### Requirement: Platform validates chunk upload integrity - -The platform SHALL validate artifact transfer ID, run session, chunk index, byte range, payload size, and chunk checksum before acknowledging an uploaded chunk. - -#### Scenario: Valid chunk is acknowledged -- **WHEN** a run endpoint uploads a chunk whose byte range, payload size, and checksum match the opened transfer -- **THEN** the platform records the chunk and returns an acknowledgement with the accepted chunk index and received chunk list - -#### Scenario: Conflicting duplicate chunk is rejected -- **WHEN** a run endpoint uploads a chunk index that was already acknowledged with different payload bytes or checksum -- **THEN** the platform rejects the request as a validation error - -### Requirement: Artifact transfer resume state is queryable - -The platform SHALL report the current transfer state, received chunk indexes, next missing chunk index, total chunk count, and completion status for an active artifact transfer. - -#### Scenario: Resume status reports missing chunk -- **WHEN** a run endpoint queries transfer status after only part of an artifact has uploaded -- **THEN** the platform returns the acknowledged chunk indexes and the next missing chunk index - -### Requirement: Artifact completion verifies full checksum - -The platform SHALL mark an artifact available only after all chunks are present and the final artifact checksum matches the opened transfer metadata. - -#### Scenario: Complete verified artifact -- **WHEN** every chunk has been uploaded and the run endpoint completes the transfer with the correct final checksum -- **THEN** the platform marks the artifact available and returns the updated artifact metadata - -#### Scenario: Missing chunk prevents completion -- **WHEN** the run endpoint completes a transfer before every chunk is present -- **THEN** the platform rejects completion and leaves the artifact non-available - -### Requirement: Run retains unacknowledged artifact chunks - -The run executor SHALL persist pending artifact chunk upload requests locally and SHALL remove a chunk from the pending queue only after platform acknowledgement for that artifact transfer and chunk index. - -#### Scenario: Acknowledged chunk is removed from retry queue -- **WHEN** a pending artifact chunk receives a platform acknowledgement for the same transfer ID and chunk index -- **THEN** the run artifact queue removes that chunk from pending retry state - -#### Scenario: Unacknowledged chunk remains pending -- **WHEN** an artifact chunk has not received a matching platform acknowledgement -- **THEN** the run artifact queue keeps the chunk available for retry diff --git a/openspec/changes/implement-artifact-transfer-channel/tasks.md b/openspec/changes/implement-artifact-transfer-channel/tasks.md deleted file mode 100644 index 8a21266..0000000 --- a/openspec/changes/implement-artifact-transfer-channel/tasks.md +++ /dev/null @@ -1,35 +0,0 @@ -## 1. Artifact Transfer Contracts - -- [x] 1.1 Add typed run artifact transfer protocol payloads in `run/protocol` for open, chunk upload, status/resume, completion, and acknowledgements. -- [x] 1.2 Add matching platform DTO/domain contracts and conversion helpers for artifact transfer requests and responses. -- [x] 1.3 Add validation rules for active upload direction, owner scope, bounded chunk size, byte ranges, chunk checksums, final checksums, and completion state. - -## 2. Platform Artifact Transfer - -- [x] 2.1 Extend platform service behavior to open upload transfers, accept idempotent chunks, reject conflicting chunks, report resume status, and mark artifacts available only after verified completion. -- [x] 2.2 Implement platform artifact transfer HTTP routes using named DTOs and service methods. -- [x] 2.3 Add platform service/API tests for successful upload, resume status, duplicate ack, checksum mismatch, invalid owner/session, and missing-chunk completion rejection. - -## 3. Run Artifact Queue And Client - -- [x] 3.1 Implement a run-side local artifact queue that writes pending chunk requests to disk, lists them for retry, and removes acknowledged chunks. -- [x] 3.2 Extend `run/api.PlatformClient` with typed artifact transfer methods. -- [x] 3.3 Add run queue/client tests for retry retention, acknowledgement cleanup, request paths, JSON payloads, response decoding, and platform error handling. - -## 4. Documentation - -- [x] 4.1 Update run and platform protocol/route documentation to mark artifact open/chunk/status/complete implemented and keep control/job/log/game-client channels separate. - -## 5. Verification - -- [x] 5.1 Run `go test ./...` from `platform/` and record evidence. -- [x] 5.2 Run `go test ./...` from `run/` and record evidence. -- [x] 5.3 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.4 Run `openspec validate implement-artifact-transfer-channel --strict` and record evidence. - -## Evidence - -- 2026-07-03: `go test ./...` from `platform/` passed. -- 2026-07-03: `go test ./...` from `run/` passed. -- 2026-07-03: `scripts/check-structure.sh` passed with `structure check passed`. -- 2026-07-03: `openspec validate implement-artifact-transfer-channel --strict` passed with `Change 'implement-artifact-transfer-channel' is valid`. diff --git a/openspec/changes/implement-browser-acceptance-suite/.openspec.yaml b/openspec/changes/implement-browser-acceptance-suite/.openspec.yaml deleted file mode 100644 index 8cceb8d..0000000 --- a/openspec/changes/implement-browser-acceptance-suite/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-08 diff --git a/openspec/changes/implement-browser-acceptance-suite/design.md b/openspec/changes/implement-browser-acceptance-suite/design.md deleted file mode 100644 index a2aa0cb..0000000 --- a/openspec/changes/implement-browser-acceptance-suite/design.md +++ /dev/null @@ -1,77 +0,0 @@ -## Context - -The architecture stream now has API-backed platform, run, platform_web, and plugin proof, plus a documented local debug workspace that can start the real stack, seed `game.example`, create `server-local-debug`, and verify a manual browser walkthrough. The remaining gap is repeatability: browser acceptance currently lives as task evidence and operator procedure, so regressions can slip in when route text, auth behavior, local fixture setup, plugin marketplace data, lifecycle operation surfaces, or safety redaction drift. - -The automated suite should preserve the existing architecture boundaries. Browser checks must exercise platform_web through platform-owned API routes, not direct run or plugin transports. Fixture setup should reuse the local debug scripts and safe dev plugin manifest instead of inventing a second seed path. The suite should be useful locally and in CI-like verification while staying scoped to the game server management platform. - -## Goals / Non-Goals - -**Goals:** -- Provide one automated browser acceptance command that verifies the API-backed local debug console path end to end. -- Reuse or orchestrate the documented local debug stack and smoke fixture so acceptance data matches the manual proof path. -- Verify login and required first-party routes: 首页、服务器管理、插件市场、用户管理、AI 提供商管理. -- Verify server detail and core plugin/server operation surfaces, including plugin marketplace data, lifecycle controls or operation-history proof, and log/artifact references. -- Fail when platform_web uses local/demo fallback data, when required API-backed evidence is missing, or when visible content exposes forbidden sensitive fragments. -- Record deterministic evidence that can be cited from OpenSpec tasks. - -**Non-Goals:** -- Do not change product behavior, visual direction, authorization semantics, or plugin/runtime contracts. -- Do not implement a cloud, billing, host marketplace, or unrelated SaaS workflow. -- Do not require Docker-only infrastructure, real game binaries, raw credentials, raw AI keys, direct sockets, or plugin/browser direct access to run. -- Do not replace unit, API, manifest, or local-debug smoke tests; this suite complements those checks by verifying browser-visible behavior. -- Do not broaden acceptance into full visual regression testing or screenshot comparison. - -## Decisions - -1. Reuse the local debug workspace as the acceptance fixture. - - The suite should either self-start the documented local debug stack or require an explicit command that does so, then run `scripts/local-debug-smoke.sh` to seed and verify the platform/run/plugin fixture before browser checks. This keeps the browser acceptance data aligned with `docs/local-debug-workspace.md` and avoids parallel fixture drift. - - Alternative considered: seed browser acceptance through a separate frontend-only mock. That would make the suite faster but would not prove real platform/run/plugin integration or prevent demo-only regressions. - -2. Put the acceptance harness near platform_web but keep stack orchestration at repository script level. - - Browser route assertions are frontend-facing and should live with platform_web tests or a clearly named acceptance harness. Starting platform, run, smoke, and frontend should remain in scripts so contributors can run one documented command without learning test internals. - - Alternative considered: hide all service orchestration inside a test file. That makes local failures harder to diagnose because logs, ports, and reset behavior become less visible than the existing local debug workflow. - -3. Assert stable user-visible and route-level signals, not screenshots. - - The suite should inspect URLs, visible text, buttons/links, and API-backed markers such as `数据已加载`, `账号 API 已连接`, `game.example`, `server-local-debug`, `run-local-debug`, lifecycle controls, and marketplace bridge actions. It should avoid brittle pixel assertions and should not depend on decorative theme details beyond preserving the existing UI test/build gates. - - Alternative considered: screenshot or visual diff acceptance. That is higher maintenance and better suited for a later design-polish workflow. - -4. Centralize fallback and forbidden-fragment scanning. - - Every route check should run a shared scanner for fallback/demo indicators and forbidden fragments: `/Users/`, `/private/`, `unix://`, `tcp://`, `Bearer `, `sk-`, `password=`, `apiKeyRef`, `rawApiKey`, run session tokens, direct run URLs, and plugin-owned transport details. Keeping this scanner shared makes new route coverage safer to add. - - Alternative considered: duplicate string checks per page. That is easy to start but likely to drift and miss new route surfaces. - -5. Produce machine-readable and human-readable evidence. - - The acceptance command should print concise pass/fail output and write an evidence artifact, such as JSON or markdown, listing routes visited, assertions passed, stack URLs, seed evidence directory, and any failure details. OpenSpec tasks should cite that evidence after the command actually runs. - - Alternative considered: rely only on terminal output. Terminal output is useful but too easy to lose during long stream handoffs. - -## Risks / Trade-offs - -- Port collisions -> Allow configurable local debug ports and make the acceptance command print resolved URLs and log paths. -- Browser automation flakiness -> Use deterministic local data, stable route URLs, bounded waits for specific page states, and concise route assertions rather than long click chains. -- Long-running service cleanup -> Reuse `scripts/local-debug-reset.sh` and ensure self-started acceptance runs stop tracked processes on success and failure. -- False positives from sensitive text inside docs or forms -> Scan only visible browser content and acceptance evidence intended for users, while still treating sensitive visible strings as failures. -- Fixture drift from smoke data -> Run or require `scripts/local-debug-smoke.sh` before browser assertions and fail if expected local debug IDs are missing. -- Sandbox limitations around localhost listeners or browser tooling -> Document when elevated permissions are needed for local listener tests, while keeping the suite command itself explicit and reproducible. - -## Migration Plan - -1. Add the browser acceptance harness and repository command wrapper. -2. Reuse the local debug smoke fixture and add evidence output for route assertions and forbidden-fragment scanning. -3. Run the new acceptance command against a clean local debug root. -4. Run frontend typecheck/tests/build, relevant local debug smoke checks, `scripts/check-structure.sh`, and strict OpenSpec validation. -5. Update stream evidence and next pointer after the change is implemented. - -Rollback is straightforward: remove the acceptance harness and command wrapper if it proves unstable; no product runtime state or persisted user data model changes are introduced. - -## Open Questions - -- None currently. During implementation, follow existing platform_web test tooling and local debug script conventions rather than adding a new browser framework if a suitable one already exists in the repository. diff --git a/openspec/changes/implement-browser-acceptance-suite/proposal.md b/openspec/changes/implement-browser-acceptance-suite/proposal.md deleted file mode 100644 index c8c8a5b..0000000 --- a/openspec/changes/implement-browser-acceptance-suite/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -## Why - -The architecture stream now has a repeatable local debug workspace and a manually verified API-backed browser walkthrough, but the walkthrough still depends on an operator remembering route checks, seed order, and forbidden-fragment scans. An automated browser acceptance suite is needed now so required first-party console flows stay real, API-backed, and safe as platform, run, platform_web, and plugin features keep evolving. - -## What Changes - -- Add an automated browser acceptance suite for the local debug/API-backed console path. -- Cover login and navigation across 首页、服务器管理、插件市场、用户管理、AI 提供商管理, plus server detail and core plugin/server operation surfaces. -- Require the suite to seed or reuse the documented local debug fixture before browser checks run. -- Require fallback/demo-only data rejection and visible forbidden-fragment scanning on every accepted route. -- Require concrete commands that can run the suite locally and in CI-like verification without relying on manual browser-only evidence. -- No breaking changes are expected; this change automates an existing verified workflow instead of changing product behavior. - -## Capabilities - -### New Capabilities -- `browser-acceptance-suite`: Defines automated browser acceptance coverage for the API-backed management console, local debug fixture prerequisites, required route assertions, plugin/server lifecycle proof, fallback rejection, forbidden-fragment scanning, and verification commands. - -### Modified Capabilities -- None. - -## Impact - -- Affected roots: `platform_web/`, `scripts/`, documentation, and potentially shared local debug fixtures under `platform/`, `run/`, and `plugins/` only as needed to support deterministic acceptance setup. -- Expected implementation areas: browser acceptance test harness, local debug stack orchestration or reuse hooks, visible content assertions, forbidden-fragment scanner, route coverage fixtures, and task evidence. -- Validation impact: requires frontend typecheck/tests/build, automated browser acceptance command, local debug smoke prerequisites, `scripts/check-structure.sh`, and `openspec validate implement-browser-acceptance-suite --strict`. diff --git a/openspec/changes/implement-browser-acceptance-suite/specs/browser-acceptance-suite/spec.md b/openspec/changes/implement-browser-acceptance-suite/specs/browser-acceptance-suite/spec.md deleted file mode 100644 index d53138a..0000000 --- a/openspec/changes/implement-browser-acceptance-suite/specs/browser-acceptance-suite/spec.md +++ /dev/null @@ -1,68 +0,0 @@ -## ADDED Requirements - -### Requirement: Acceptance suite runs against the real local debug stack -The repository SHALL provide an automated browser acceptance suite that verifies platform_web against the API-backed local debug stack and safe game plugin fixture. - -#### Scenario: Suite prepares local debug fixture -- **WHEN** the acceptance suite is run from a clean checkout with documented local debug prerequisites -- **THEN** it MUST start or reuse platform, run worker, platform_web, and the dev game plugin fixture through documented local debug commands and MUST seed or verify `game.example`, `server-local-debug`, and `run-local-debug` before browser assertions begin - -#### Scenario: Suite uses platform-owned browser API path -- **WHEN** platform_web is exercised by the acceptance suite -- **THEN** browser requests MUST go through the configured platform API proxy with `VITE_PLATFORM_API_BASE_URL=/api/v1` and MUST NOT require direct run URLs, run credentials, or plugin-owned transports in browser code or visible output - -### Requirement: Acceptance suite verifies first-party console areas -The browser acceptance suite SHALL verify the required first-party platform_web areas with an API-backed user session. - -#### Scenario: Suite logs in with local debug user -- **WHEN** the acceptance suite opens platform_web -- **THEN** it MUST log in with the documented local debug operator account and confirm the session lands on an API-backed workspace rather than local fallback data - -#### Scenario: Suite verifies required routes -- **WHEN** browser acceptance route checks run -- **THEN** they MUST open 首页、服务器管理、插件市场、用户管理、AI 提供商管理 and assert stable API-backed content for each route - -#### Scenario: Suite rejects fallback content -- **WHEN** any required route renders fallback, mock, demo-only, or local-auth fallback content -- **THEN** the acceptance suite MUST fail and report the route, visible evidence, and failed assertion - -### Requirement: Acceptance suite verifies plugin and server operation surfaces -The browser acceptance suite SHALL verify core plugin/server operation surfaces that prove the console is connected to platform-mediated lifecycle and plugin data. - -#### Scenario: Suite verifies server detail lifecycle surface -- **WHEN** the suite opens the local debug server detail route -- **THEN** it MUST assert that `Local Debug Example Server`, `server-local-debug`, `game.example`, `run-local-debug`, lifecycle controls, operation history, log entry points, and artifact or artifact-reference entry points are visible or otherwise represented through platform-owned UI state - -#### Scenario: Suite verifies plugin marketplace data -- **WHEN** the suite opens the plugin marketplace route or plugin detail surface -- **THEN** it MUST assert that `game.example`, manifest reference metadata, installed state, platform-mediated permissions, bridge actions, and lifecycle capabilities are visible without exposing unsafe runtime transport details - -#### Scenario: Suite verifies operation proof without direct run access -- **WHEN** the suite triggers or inspects a lifecycle operation -- **THEN** it MUST verify platform-owned job or operation-history evidence and MUST NOT rely on platform_web or plugin pages contacting run directly - -### Requirement: Acceptance suite scans visible safety boundaries -The browser acceptance suite SHALL scan accepted browser-visible content for fallback indicators and forbidden sensitive fragments. - -#### Scenario: Suite scans each accepted route -- **WHEN** a required route or plugin/server operation surface is accepted -- **THEN** the suite MUST scan visible text for `/Users/`, `/private/`, `unix://`, `tcp://`, `Bearer `, `sk-`, `password=`, `apiKeyRef`, `rawApiKey`, run session tokens, direct run URLs, and plugin-owned transport details - -#### Scenario: Suite fails on forbidden visible fragments -- **WHEN** any forbidden sensitive fragment is visible on an accepted route -- **THEN** the suite MUST fail and report the route, matched fragment class, and enough nearby evidence to debug the leak without printing raw credentials - -### Requirement: Acceptance suite produces reproducible evidence -The browser acceptance suite SHALL provide concrete commands and evidence outputs that can be used to close OpenSpec tasks. - -#### Scenario: Suite command is documented -- **WHEN** contributors read the change documentation or tasks -- **THEN** they MUST find concrete commands for running local debug smoke, browser acceptance, frontend checks, structure checks, and strict OpenSpec validation - -#### Scenario: Suite writes acceptance evidence -- **WHEN** browser acceptance passes -- **THEN** it MUST write or print evidence including stack URLs, seed evidence directory, routes checked, required assertions, fallback scan results, forbidden-fragment scan results, and plugin/server operation proof - -#### Scenario: Suite cleans up self-started services -- **WHEN** the suite starts local debug services itself -- **THEN** it MUST stop or reset only the documented local debug root after completion or failure, using the same safe reset scope as the local debug workspace diff --git a/openspec/changes/implement-browser-acceptance-suite/tasks.md b/openspec/changes/implement-browser-acceptance-suite/tasks.md deleted file mode 100644 index f19ffc3..0000000 --- a/openspec/changes/implement-browser-acceptance-suite/tasks.md +++ /dev/null @@ -1,65 +0,0 @@ -## 1. Acceptance Harness and Command Shape - -- [x] 1.1 Add an automated browser acceptance harness in the existing platform_web test/tooling structure, keeping route assertions near frontend code and stack orchestration in repository scripts. -- [x] 1.2 Add a repository command wrapper for running browser acceptance against the local debug stack, with configurable `LOCAL_DEBUG_PLATFORM_PORT`, `LOCAL_DEBUG_WEB_PORT`, and `LOCAL_DEBUG_ROOT`. -- [x] 1.3 Ensure the command can self-start or explicitly reuse the documented local debug stack, and records the resolved platform URL, platform_web URL, log paths, and evidence directory. -- [x] 1.4 Ensure self-started runs clean up with `scripts/local-debug-reset.sh` and only remove allowed local debug roots. - -## 2. Local Debug Fixture Prerequisites - -- [x] 2.1 Reuse `scripts/local-debug-smoke.sh` or equivalent platform-owned setup before browser assertions so `game.example`, `server-local-debug`, and `run-local-debug` exist. -- [x] 2.2 Fail early when platform health, API login, plugin manifest validation, plugin registration, run heartbeat, server lifecycle fixture creation, or job/log/artifact/marketplace references are missing. -- [x] 2.3 Preserve the existing browser/API boundary: platform_web must use `PLATFORM_API_PROXY` and `VITE_PLATFORM_API_BASE_URL=/api/v1`, with `VITE_ENABLE_LOCAL_AUTH_FALLBACK=false`. -- [x] 2.4 Keep fixture commands harmless and bounded, with no Docker-only dependency, real game binaries, raw credentials, raw AI keys, direct sockets, or browser/plugin direct access to run. - -## 3. Browser Route Assertions - -- [x] 3.1 Automate login at platform_web with `operator.local@example.test` / `operator-local` and verify the session lands on an API-backed workspace. -- [x] 3.2 Verify 首页 `#/home` includes API-backed platform overview signals such as `平台概览`, `数据已加载`, game/plugin instance counts, and run node state. -- [x] 3.3 Verify 服务器管理 `#/servers` includes `Local Debug Example Server` and `server-local-debug`. -- [x] 3.4 Verify 插件市场 `#/plugins` includes `game.example`, installed state, manifest reference metadata, lifecycle capabilities, platform-mediated permissions, and bridge actions. -- [x] 3.5 Verify 用户管理 `#/users` includes API-connected account data for `operator.local@example.test`. -- [x] 3.6 Verify AI 提供商管理 `#/aiProviders` includes API-backed provider rows with redacted key references only. - -## 4. Plugin and Server Operation Surface Assertions - -- [x] 4.1 Verify server detail `#/servers/server-local-debug` includes `Local Debug Example Server`, `game.example@0.1.0`, `run-local-debug`, lifecycle controls, logs, config, plugin controls, AI assistant, and operation history entry points. -- [x] 4.2 Trigger or inspect a platform-mediated lifecycle operation and verify platform-owned job or operation-history evidence without requiring direct run access from browser or plugin pages. -- [x] 4.3 Verify log and artifact entry points are represented by logical IDs, platform routes, log refs, artifact refs, or safe metadata only. -- [x] 4.4 Record route-level assertion results in machine-readable evidence, including URL, required markers, and plugin/server operation proof. - -## 5. Safety and Fallback Scanning - -- [x] 5.1 Add a shared fallback scanner that fails on local/demo/fallback workspace indicators on all accepted routes. -- [x] 5.2 Add a shared visible-content forbidden-fragment scanner for `/Users/`, `/private/`, `unix://`, `tcp://`, `Bearer `, `sk-`, `password=`, `apiKeyRef`, `rawApiKey`, run session tokens, direct run URLs, and plugin-owned transport details. -- [x] 5.3 Ensure scanner failures report the route, matched fragment class, and safe nearby evidence without printing raw credentials. -- [x] 5.4 Confirm sensitive values remain hidden from browser-visible output while safe redacted references such as `secret://...` or `env://...` are allowed when expected. - -## 6. Documentation and Verification - -- [x] 6.1 Document the browser acceptance command and expected evidence output in the appropriate local debug or frontend development documentation. -- [x] 6.2 Run `LOCAL_DEBUG_PLATFORM_PORT=18189 LOCAL_DEBUG_WEB_PORT=5183 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-acceptance ` and record the exact final command after implementation. -- [x] 6.3 Run `LOCAL_DEBUG_PLATFORM_PORT=18189 LOCAL_DEBUG_WEB_PORT=5183 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-acceptance scripts/local-debug-smoke.sh` or document why the acceptance command already ran the same smoke prerequisite. -- [x] 6.4 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 6.5 Run relevant touched-root checks, including `cd plugins && npm run typecheck && npm run test && npm run validate:manifest`, `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1`, and `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` when implementation touches those roots or local debug orchestration. -- [x] 6.6 Run `scripts/check-structure.sh` and record evidence. -- [x] 6.7 Run `openspec validate implement-browser-acceptance-suite --strict` and record evidence. -- [x] 6.8 Update `openspec/changes/architecture-delivery-stream/delivery-plan.md` and `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` after implementation evidence exists, then stop without implementing `polish-platform-interaction-design` unless explicitly asked. - -## Evidence - -- Implemented `platform_web/acceptance/browser-acceptance.mjs`, `scripts/browser-acceptance.sh`, `platform_web` package script `acceptance:browser`, and documentation in `docs/local-debug-workspace.md` plus `platform_web/README.md`. -- `node --check platform_web/acceptance/browser-acceptance.mjs` passed. -- `bash -n scripts/browser-acceptance.sh` passed. -- `LOCAL_DEBUG_PLATFORM_PORT=18189 LOCAL_DEBUG_WEB_PORT=5183 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-acceptance scripts/browser-acceptance.sh` passed after clearing a stale listener from an earlier interrupted run. -- Browser acceptance evidence: `/private/tmp/browser-local-debug-acceptance/browser-acceptance/browser-acceptance-evidence.json`, checked at `2026-07-08T05:13:43.494Z`. -- Acceptance command ran `scripts/local-debug-smoke.sh` as a prerequisite and wrote seed evidence under `/private/tmp/browser-local-debug-acceptance/smoke`. -- Browser routes verified: 首页, 服务器管理, 插件市场, 用户管理, AI 提供商管理, 服务器详情, and 服务器详情 / 插件控制. -- Operation proof verified platform API accepted `process.start` for `server-local-debug`, platform-owned jobs endpoint returned `server-lifecycle:server-local-debug:start:e93f12eb71c03646`, and browser operation history exposed platform task records without direct run access. -- `cd platform_web && npm run typecheck && npm test && npm run build` passed: 11 test files and 49 tests passed, Vite production build succeeded. -- `cd plugins && npm run typecheck && npm run test` passed: 1 test file and 11 tests passed. -- `cd plugins && npm run validate:manifest` passed after an escalated rerun because sandboxed `tsx` IPC failed with `listen EPERM`. -- `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` passed. -- `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` passed after an escalated rerun because sandboxed `httptest` localhost binding failed with `listen tcp6 [::1]:0: bind: operation not permitted`. -- `scripts/check-structure.sh` passed. -- `openspec validate implement-browser-acceptance-suite --strict` passed. OpenSpec emitted PostHog DNS flush warnings after validation, but the command exited 0 and reported `Change 'implement-browser-acceptance-suite' is valid`. diff --git a/openspec/changes/implement-config-write-and-file-dispatch/.openspec.yaml b/openspec/changes/implement-config-write-and-file-dispatch/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-config-write-and-file-dispatch/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-config-write-and-file-dispatch/design.md b/openspec/changes/implement-config-write-and-file-dispatch/design.md deleted file mode 100644 index aa44c17..0000000 --- a/openspec/changes/implement-config-write-and-file-dispatch/design.md +++ /dev/null @@ -1,59 +0,0 @@ -## Context - -The frontend already has a useful diff review UX, but it dispatches config writes by calling `POST /jobs` with `capability=config.write`. That bypasses platform-owned config validation, stale version checks, and scoped file semantics. - -This change creates a platform service boundary for config/file dispatch. Later run worker and plugin bridge changes can execute the queued jobs using the same safe envelopes. - -## Goals / Non-Goals - -**Goals:** - -- Add config diff preview and approval routes. -- Validate expected config version, bounded content size, logical config keys, and server ACLs. -- Queue scoped run jobs for approved config/file operations. -- Update frontend to call approval routes rather than generic job creation. -- Keep all write operations reviewable and auditable. - -**Non-Goals:** - -- No real process execution or local file mutation in this change. -- No unrestricted file manager or raw path API. -- No plugin page bridge execution beyond shared dispatch contracts. -- No external storage backend implementation. - -## Decisions - -### Decision 1: Platform service owns write approval - -Config write approval is a service method, not a frontend-generated generic job. It validates server access, config version, diff payload, and logical file key before creating a queued job. - -### Decision 2: File operations use logical keys and refs - -Requests identify server-scoped config/file targets by logical keys or artifact/input refs. Raw absolute paths, home directories, sockets, and credentials are rejected. - -### Decision 3: Diff preview can be pure platform computation - -The backend can compute a textual diff from current config and proposed content without dispatching work. Approval is a separate explicit call. - -### Decision 4: Frontend keeps second confirmation - -ServerDetailPage and AI suggestion flows must keep an explicit confirmation after showing the diff. Approval dispatch happens only after that confirmation. - -## Risks / Trade-offs - -- [Risk] Queued jobs may not execute until the real run worker change lands. Mitigation: this change verifies dispatch and state, not local mutation. -- [Risk] Diff preview duplicates frontend diff code. Mitigation: frontend diff remains display-oriented; backend diff validates dispatch input. -- [Risk] Logical file keys may be too narrow. Mitigation: keep schema extensible and add cases through future OpenSpec changes. - -## Migration Plan - -1. Add platform contracts and validators for config diff/write and file dispatch. -2. Add API handlers and route docs. -3. Extend run protocol payload validation for scoped config/file jobs. -4. Update ServerDetailPage config and AI write flows. -5. Add backend/frontend/run tests and walkthrough. - -## Open Questions - -- Whether approved config writes should become a first-class operation resource rather than a job-only response. -- Whether future restart/update/delete workflows should share the same approval envelope. diff --git a/openspec/changes/implement-config-write-and-file-dispatch/proposal.md b/openspec/changes/implement-config-write-and-file-dispatch/proposal.md deleted file mode 100644 index 6cb700c..0000000 --- a/openspec/changes/implement-config-write-and-file-dispatch/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -ServerDetailPage currently previews config diffs locally and then creates a generic `config.write` job directly from the browser. The route catalog still lists config diff review and file operation dispatch as future work. Operators need a platform-mediated, auditable flow that validates config versions and dispatches scoped run jobs without leaking host paths or credentials. - -## What Changes - -- Add backend config diff preview and config write approval workflows. -- Add scoped file operation dispatch contracts for safe file read/write jobs. -- Move config write dispatch out of generic frontend job creation and into platform-owned service methods. -- Keep explicit user confirmation before any write job is dispatched. -- Update frontend config and AI suggestion write paths to use approved platform routes. - -## Capabilities - -### New Capabilities - -- `config-write-and-file-dispatch`: Safe config diff review, approval, and scoped file operation dispatch. - -### Modified Capabilities - -- `server-management-workflows`: Server detail config writes use platform lifecycle/file dispatch rules instead of direct generic job creation. - -## Impact - -- Affects `platform/` domain, DTO, validators, service, API handlers, route/protocol docs, and tests. -- Affects `run/` protocol validation for scoped config/file job payloads. -- Affects `platform_web/` ServerDetailPage config and AI suggestion apply flows. -- Does not add unrestricted file browsing, raw path exposure, billing, cloud sales, or direct plugin-to-run access. diff --git a/openspec/changes/implement-config-write-and-file-dispatch/specs/config-write-and-file-dispatch/spec.md b/openspec/changes/implement-config-write-and-file-dispatch/specs/config-write-and-file-dispatch/spec.md deleted file mode 100644 index b04ad44..0000000 --- a/openspec/changes/implement-config-write-and-file-dispatch/specs/config-write-and-file-dispatch/spec.md +++ /dev/null @@ -1,45 +0,0 @@ -## ADDED Requirements - -### Requirement: Config diff preview is platform mediated -The platform SHALL provide a config diff preview route that compares current server config with proposed content without dispatching a write. - -#### Scenario: Preview accepted -- **WHEN** an authorized operator submits proposed config content with the current config version -- **THEN** the platform MUST return a bounded diff and MUST NOT create a run job - -#### Scenario: Preview rejects stale config -- **WHEN** proposed config content references a stale config version -- **THEN** the platform MUST reject the preview and MUST NOT dispatch work - -### Requirement: Config write approval dispatches scoped job -The platform SHALL dispatch config writes only after an explicit approval request passes validation. - -#### Scenario: Approved config write queued -- **WHEN** an authorized operator approves a reviewed config diff -- **THEN** the platform MUST queue a bounded `config.write` job for the server instance and return job metadata - -#### Scenario: Config write hides unsafe internals -- **WHEN** the platform dispatches a config write job -- **THEN** the request and response MUST NOT expose raw host paths, run credentials, direct sockets, or raw secret values - -### Requirement: File operation dispatch is scoped -The platform SHALL provide scoped file operation dispatch for server/plugin workflows using logical file keys or artifact refs. - -#### Scenario: Scoped file read dispatched -- **WHEN** an authorized caller requests a declared logical file read -- **THEN** the platform MUST queue a bounded file read job with scoped target metadata - -#### Scenario: Unsafe file target rejected -- **WHEN** a request includes an absolute path, parent traversal, raw credential, direct socket, or host-local secret path -- **THEN** the platform MUST reject the request before creating a job - -### Requirement: Frontend config writes use approval APIs -The frontend SHALL use platform config preview and approval APIs for config writes. - -#### Scenario: User previews and approves config write -- **WHEN** a user edits config, previews the diff, and confirms approval -- **THEN** ServerDetailPage MUST call the approval API and render the returned platform job state - -#### Scenario: Frontend avoids generic write job creation -- **WHEN** a config write is initiated from manual edit or AI suggestion -- **THEN** the frontend MUST NOT create a generic `config.write` job directly through `POST /jobs` diff --git a/openspec/changes/implement-config-write-and-file-dispatch/tasks.md b/openspec/changes/implement-config-write-and-file-dispatch/tasks.md deleted file mode 100644 index 51c90c0..0000000 --- a/openspec/changes/implement-config-write-and-file-dispatch/tasks.md +++ /dev/null @@ -1,58 +0,0 @@ -## 1. Config Diff Review Contracts - -- [x] 1.1 Add domain contracts for config diff review, proposed content, approval status, and dispatch metadata. -- [x] 1.2 Add DTO contracts for config diff preview, approval, rejection, and dispatch responses. -- [x] 1.3 Add validators for bounded config size, allowed file keys, expected config version, and diff content safety. -- [x] 1.4 Add service methods for previewing and approving config writes without exposing host paths. - -## 2. File Operation Dispatch Contracts - -- [x] 2.1 Add domain/DTO contracts for scoped file read/write requests. -- [x] 2.2 Map file operations to platform job capabilities such as `config.write`, `files.read`, and `files.write`. -- [x] 2.3 Enforce plugin/server permissions and role-scoped server access before dispatch. -- [x] 2.4 Ensure dispatch payloads use logical file keys or artifact/input refs, not raw host paths. - -## 3. Backend API Surface - -- [x] 3.1 Implement config diff preview route for a server instance. -- [x] 3.2 Implement config write approval route that queues a bounded run job. -- [x] 3.3 Implement file operation dispatch route for scoped plugin/platform file jobs. -- [x] 3.4 Update route/protocol documentation to mark config and file dispatch implemented. - -## 4. Frontend Integration - -- [x] 4.1 Update ServerDetailPage config write flow to call config diff preview API. -- [x] 4.2 Update confirmation flow to call config write approval API instead of creating a generic job directly. -- [x] 4.3 Keep explicit second confirmation before dispatching any config write. -- [x] 4.4 Remove local-only config mutation after job dispatch; show pending platform job state instead. - -## 5. Run Integration Prep - -- [x] 5.1 Extend run protocol job payloads to carry scoped config/file input refs. -- [x] 5.2 Add run-side validation for allowed logical paths and bounded write payloads. -- [x] 5.3 Add tests proving raw host paths and credentials are rejected. - -## 6. Verification - -- [x] 6.1 Add platform tests for preview, approval, stale config version, unauthorized server access, and unsafe paths. -- [x] 6.2 Add frontend tests for diff preview, approval, failure, and no local mutation on dispatch. -- [x] 6.3 Run platform, run, and platform_web test/build commands and record evidence. -- [x] 6.4 Run browser walkthrough for config diff and write approval. -- [x] 6.5 Run `scripts/check-structure.sh` and record evidence. -- [x] 6.6 Run `openspec validate implement-config-write-and-file-dispatch --strict` and record evidence. - -## Evidence - -- 2026-07-06: `cd platform && go test ./domain ./dto ./validator ./service ./api ./model` passed after adding config diff/write contracts, validators, service methods, and API handlers. -- 2026-07-06: `cd run && go test ./protocol` passed after adding scoped job target/input refs and run protocol validation tests for raw host paths and raw credential refs. -- 2026-07-06: Updated `platform/api/routes.md`, `platform/protocol/server-lifecycle.md`, `run/protocol/job.md`, and `platform_web/api/contracts.md` to document implemented config diff/approval and scoped file dispatch routes/protocol payloads. -- 2026-07-06: `cd platform && go test ./service ./api -run 'TestCoreServiceConfigWriteAndFileDispatchAreScoped|TestConfigWriteAndFileDispatchAPIAreScoped'` passed, covering preview, approval, stale config version, unauthorized access, unsafe keys, and scoped file dispatch. -- 2026-07-06: `cd platform_web && npm run typecheck` passed after adding config diff/approval/file dispatch API types and client methods. -- 2026-07-06: `cd platform_web && npm test -- --run api/client.test.ts pages/ServerDetailPage.test.tsx` passed, covering preview/approval client requests, preview failure surfacing, platform diff mapping, no generic `config.write` job creation, and no local config mutation after approval dispatch. -- 2026-07-06: `cd platform && go test ./...` passed. -- 2026-07-06: `cd run && go test ./...` passed. -- 2026-07-06: `cd platform_web && npm test` passed with 11 files / 40 tests. -- 2026-07-06: `cd platform_web && npm run build` passed. -- 2026-07-06: Browser walkthrough passed using local platform `127.0.0.1:18090`, Vite `127.0.0.1:5177`, and headless Chrome: logged in, opened `#/servers/server-walkthrough`, edited config, previewed the platform diff, confirmed approval dispatch, saw the returned `config.write` job badge, and verified no `/Users/`, `unix://`, bearer token, raw key, password, or billing fragments were visible. -- 2026-07-06: `scripts/check-structure.sh` passed. -- 2026-07-06: `openspec validate implement-config-write-and-file-dispatch --strict` reported `Change 'implement-config-write-and-file-dispatch' is valid`; PostHog telemetry flush failed due restricted DNS and did not affect validation. diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/.openspec.yaml b/openspec/changes/implement-dependency-installation-and-run-self-update/.openspec.yaml deleted file mode 100644 index ff5f854..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-17 diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/design.md b/openspec/changes/implement-dependency-installation-and-run-self-update/design.md deleted file mode 100644 index d3c611a..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/design.md +++ /dev/null @@ -1,84 +0,0 @@ -## Context - -Task 05 made lifecycle/config/file jobs real and Task 06 made logs, artifacts, metrics, backups, and remote adapters durable. Distribution APIs already create `dependencies.check`, `dependencies.install`, and `run.self-update` jobs, but Run currently returns immediate synthetic success. Platform also accepts any available same-owner-visible artifact for an update and has no Run-only artifact read route, immutable dependency-plan approval, terminal dependency projection, or update activation journal. - -The implementation spans the main repository and the independent `run/` repository. Platform remains authoritative for user ownership/admin scope, installed plugin/version, server/runtime binding, selected endpoint, Run session/signature, attempt/lease/cancel fencing, artifact ownership, immutable plan digest, and audit. Run owns machine-local resolution, typed adapter execution, staging, replacement, health confirmation, and rollback. Plugins declare safe logical plans; platform_web receives only safe projections. - -## Goals / Non-Goals - -**Goals:** - -- Execute declared dependency probes and install plans through fixed, testable adapters with bounded time, output, retries, and cancellation. -- Make the exact platform/architecture-specific plan reviewable and bind installation approval to its digest. -- Persist dependency status and update phases across Platform and Run restarts. -- Download only an approved same-server Run distribution through a signed, active-attempt-only, resumable contract. -- Verify archive and binary bounds/checksums, preserve the current package configuration, stage durably, activate only after the terminal result is accepted, confirm startup health, and roll back on failure. -- Keep heartbeat, job ack/result/cancel polling, logs, and artifact upload independent from slow dependency/update work. - -**Non-Goals:** - -- Arbitrary shell/script execution, user-supplied command vectors, generic root package management, unverified HTTP downloads, or undeclared host/credential access. -- Production code-signing/KMS, release rings, fleet rollout, centralized binary mirrors, dependency solving, Run service-manager installation, client-manager lifecycle, plugin lifecycle, production scaling/alerts, or real AI-provider integration. - -## Decisions - -### Decision 1: Platform snapshots declared inputs and approves an immutable digest - -Platform derives a safe dependency catalog from the installed plugin version, selected runtime profile, endpoint OS/architecture, and declared probes/plans. A canonical digest covers the declaration and non-secret logical binding generation. An install request must include the digest returned by the catalog. Dispatch re-resolves the declaration and rejects changed plugin versions, plan steps, target platform, bindings, endpoint, or digest before creating the job. - -Alternative considered: accept only a plan key and resolve it at execution time. Rejected because an operator could approve one plan and execute a later plugin revision. - -### Decision 2: Private execution inputs use active fenced Run routes - -Dependency declarations/resolved target values and Run update manifests are not placed in browser-visible job DTOs. Run retrieves them through signed `/api/v1/run/jobs/dependency-input`, `/api/v1/run/jobs/update-input`, and `/api/v1/run/jobs/update-chunk` routes carrying endpoint, session, job, attempt, and lease. Platform uses the existing fenced-job check and requires an active matching capability. Update chunks are bounded and range-addressed; no browser download token or raw storage path is returned. - -Alternative considered: embed all data in `Job.ExecutionInput.Content`. Rejected because it weakens type separation and increases the chance of private binding or package data entering general job projections. - -### Decision 3: Dependency work uses a closed adapter registry - -Run maps probe kinds and install step types to fixed implementations. Package steps map a whitelisted manager to fixed argument builders and validate package/version tokens. Verified downloads require HTTPS, a declared SHA-256 checksum, a size limit, and a scoped destination. SteamCMD uses a fixed executable/argument shape. Manual steps return a safe blocked result and never claim installation. No adapter invokes a shell, evaluates manifest text, accepts environment overrides, or returns command output/paths. - -The executor has injected command/download/filesystem interfaces for deterministic tests; production implementations use `exec.CommandContext`, bounded HTTPS, owner-only workspaces, and atomic files. - -Alternative considered: translate declarations into shell scripts. Rejected because shell parsing defeats the declared capability boundary. - -### Decision 4: Dependency state is projected from terminal evidence - -Run returns a typed execution result containing only declaration key, present/missing/installed/failed classification, bounded version evidence, completed step count, and plan digest. Platform verifies that evidence against the job snapshot before updating `DependencyStatus`. Attempts and local journals are idempotent; retry/cancel/stale results remain governed by the existing scheduler. Audit summaries never include resolved paths, commands, package-manager output, or credentials. - -### Decision 5: Self-update is a durable two-process transaction - -Run streams the approved distribution archive into an owner-only transaction directory, persists offset/hash metadata, verifies the final artifact checksum, safely extracts exactly one expected Run binary, and records a staged manifest. Archive traversal, links, devices, duplicate executables, excess entries, oversized bodies, target mismatch, and bundled configuration replacement are rejected. - -After Platform accepts the successful staged job result and the Run journal has persisted the acknowledgement, the worker launches the staged binary in helper mode and exits. The helper waits for the old PID, backs up the current executable, copies the staged binary through an atomic temporary target, starts the new executable with helper-only environment removed, and waits for a startup-health marker. The new worker writes that marker and sends a signed update-health report only after registration and job reconciliation succeed; Platform keeps the safe phase at `restart-requested/activating` until that report matches the terminal update job, endpoint, attempt, lease proof, target release, and current session. Failure restores the backup and restarts the previous binary. The package's existing `config.json` remains untouched. - -Alternative considered: replace the executable before reporting the job. Rejected because Platform could retain a running lease with no terminal result. Alternative considered: report success after merely staging. Rejected because the update record would misrepresent activation; the safe projection distinguishes `staged/restart-requested`, `activating`, `succeeded`, `rolled-back`, and `failed` phases. - -### Decision 6: Recovery is driven by journals, not process memory - -Dependency executions store completed step indexes and immutable digests under the scoped workspace. Update transactions store artifact offset, expected checksum, staged binary checksum, current/backup logical locations, phase, attempt, and timestamps. Startup recovery removes invalid partial data, resumes eligible downloads, confirms a healthy activated transaction, or rolls back an interrupted activation. Attempt/lease values are used for fencing but never exposed in safe status or logs. - -### Decision 7: Endpoint target identity and channel priority remain explicit - -Run endpoint records persist OS/architecture from hello so Platform can reject cross-target distributions and plans. Downloading dependencies or update chunks occurs inside the claimed job goroutine; heartbeat and durable log/artifact upload loops remain separate. Progress/cancel polling uses bounded contexts. Tests block download/adapters while asserting heartbeat, ack/result, and log upload deadlines. - -## Risks / Trade-offs - -- [Package managers vary across distributions and may require privilege] → Validate the endpoint OS, use manager-specific fixed arguments, surface a safe permission failure, and never auto-escalate through sudo/shell. -- [A process can crash between staged result and helper activation] → Persist the post-ack activation request and recover it at startup; Platform distinguishes staging from confirmed version/health. -- [Windows executable replacement differs from Unix rename behavior] → Helper copies from the staged executable after the parent exits and uses backup/temporary targets instead of renaming a running binary. -- [A newly started binary can launch but fail registration] → New Run writes health only after successful registration/reconciliation; helper times out and restores the previous executable. -- [Old records lack endpoint OS/architecture or plan digests] → Existing endpoints re-register before real actions become available; legacy queued placeholder jobs are not retroactively executed. -- [Large update archives can consume disk/network] → Enforce artifact/archive/binary limits, bounded chunks, resumable offsets, owner-only roots, and cleanup after terminal retention. - -## Migration Plan - -1. Add backward-compatible endpoint target fields, dependency/update records, DTOs, protocols, repositories, and private routes. -2. Require endpoint re-registration to advertise a supported OS/architecture before enabling dependency install or self-update. -3. Publish safe dependency catalog/status and update phase projections; existing generic job views remain compatible. -4. Enable real Run capabilities only when the typed executors and journals initialize successfully. -5. On rollback, stop advertising the real capabilities and leave private journals/artifacts for a compatible binary to recover; do not delete or reinterpret prior Platform records. - -## Open Questions - -- Production signing policy and rollout rings remain a future change; this task enforces artifact ownership, target match, content checksum, and optional signature metadata without claiming a production PKI. diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/proposal.md b/openspec/changes/implement-dependency-installation-and-run-self-update/proposal.md deleted file mode 100644 index 6eb2b5c..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/proposal.md +++ /dev/null @@ -1,32 +0,0 @@ -## Why - -Platform can currently queue dependency and Run update jobs, but Run returns synthetic success without executing a declared install step or downloading, verifying, staging, activating, and recovering an update. Operators therefore see completion for work that did not happen, and the existing job/artifact security boundaries are not yet sufficient for real machine mutation. - -## What Changes - -- Resolve plugin-declared dependency probes and install plans through Platform ownership, installed-plugin, runtime-profile, binding, endpoint, platform, session, attempt, and lease checks. -- Expose a safe, reviewable dependency catalog and require approval of the exact immutable plan digest before dispatching an install. -- Execute only typed package, verified-download, and SteamCMD steps through fixed adapters; reject arbitrary shell, scripts, unsafe package arguments, unapproved downloads, undeclared targets, stale plans, and unsupported operating systems. -- Persist dependency execution status/evidence and project terminal job results into bounded, redacted Platform records and UI status. -- Add a Run-only, fenced, resumable artifact download contract for approved same-server Run distributions. -- Download, checksum-verify, safely extract, stage, and durably journal Run updates; activate them through a post-result helper, verify startup health, and roll back on activation failure. -- Persist Run update phases and audit outcomes without exposing host paths, Run/session/lease tokens, secret refs, credentials, PIDs, sockets, or private plan bindings. -- Preserve control/job/log/artifact channel isolation so dependency downloads and update transfer/activation do not delay heartbeat, job acknowledgement/result, cancellation polling, or log upload. - -## Capabilities - -### New Capabilities - -- `durable-dependency-execution`: reviewable declared dependency plans, fenced Run input, typed probes/install adapters, persistence, recovery, cancellation, and safe projections. -- `transactional-run-self-update`: approved artifact download, resumable verification, durable staging, post-result activation, startup health confirmation, rollback, and audit semantics. - -### Modified Capabilities - -- `run-distribution-and-client-managers`: dependency and self-update jobs now perform real bounded machine work instead of success-only hooks. -- `artifact-transfer-channel`: authenticated Run jobs can read approved distribution artifacts in bounded resumable chunks without using browser download sessions. - -## Impact - -- Affects `plugins/` dependency declaration validation/SDK examples, `platform/` domain/DTO/model/repository/service/protocol/validator/API layers, projection-only `platform_web/` dependency/update status, and the independent `run/` protocol/runtime/config/shared layers. -- Adds no arbitrary shell capability and no raw host path, credential, socket, token, lease, session hash, or secret projection to plugins or platform_web. -- Does not implement client-manager lifecycle, dependency installation outside declared adapters, Run distribution signing infrastructure/KMS, production rollout rings/fleet orchestration, plugin lifecycle, production scaling/alerts, or real AI-provider integration. diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/artifact-transfer-channel/spec.md b/openspec/changes/implement-dependency-installation-and-run-self-update/specs/artifact-transfer-channel/spec.md deleted file mode 100644 index c44e7ac..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/artifact-transfer-channel/spec.md +++ /dev/null @@ -1,16 +0,0 @@ -## ADDED Requirements - -### Requirement: Active Run update jobs can read approved artifact ranges -The artifact channel SHALL provide a signed, bounded, resumable read contract exclusively for an active fenced `run.self-update` attempt whose artifact is an available same-server target-matched Run distribution. - -#### Scenario: Run reads the next update range -- **WHEN** Run presents the selected endpoint/session/job/attempt/lease and a valid offset and length -- **THEN** Platform MUST return only that bounded artifact range plus artifact ID, offset, total size, checksum, and completion metadata - -#### Scenario: Run requests unrelated artifact data -- **WHEN** the job is inactive, the artifact/distribution/server/endpoint/target differs, or the range exceeds bounds -- **THEN** Platform MUST reject the request without returning bytes, paths, credentials, browser download sessions, secret refs, or cross-owner metadata - -#### Scenario: Update transfer is slow -- **WHEN** an update range read or network response is blocked -- **THEN** control, job ack/result/cancel, log ingest, and independent artifact upload routes MUST continue without waiting on the read diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/durable-dependency-execution/spec.md b/openspec/changes/implement-dependency-installation-and-run-self-update/specs/durable-dependency-execution/spec.md deleted file mode 100644 index b704101..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/durable-dependency-execution/spec.md +++ /dev/null @@ -1,68 +0,0 @@ -## ADDED Requirements - -### Requirement: Dependency plans are declared and reviewable -Platform SHALL derive a safe dependency catalog from the installed plugin version, selected runtime profile, complete binding, and Run target, and SHALL require approval of the exact plan digest before installation. - -#### Scenario: Operator reviews an install plan -- **WHEN** an authorized owner or platform administrator queries dependency actions for a server -- **THEN** Platform MUST return declared probe keys, plan titles, target OS/architecture, typed step summaries, current safe status, and a deterministic plan digest without host paths, commands, credentials, secret refs, sockets, tokens, leases, sessions, hashes used for fencing, or PIDs - -#### Scenario: Approved plan changes before dispatch -- **WHEN** the plugin version, runtime profile, target, binding generation, plan steps, or digest no longer matches the reviewed plan -- **THEN** Platform MUST reject installation and record a safe denied audit event before creating a job - -#### Scenario: Caller crosses server ownership -- **WHEN** a non-owner without server-admin or platform-admin scope requests a catalog, check, or install -- **THEN** Platform MUST return the existing unauthorized/forbidden semantics and MUST NOT reveal whether private bindings or plans exist - -### Requirement: Dependency execution input is fenced and private -Run SHALL receive dependency declarations and resolved target values only through a signed Platform route scoped to the active endpoint, session, job, attempt, and lease. - -#### Scenario: Active Run loads dependency input -- **WHEN** the selected Run requests input for its active dependency attempt -- **THEN** Platform MUST verify endpoint ownership, session/signature, job capability/state, attempt/lease, server/plugin/profile/target, immutable digest, and cancellation state before returning the bounded typed input - -#### Scenario: Stale or cross-endpoint Run requests input -- **WHEN** the endpoint, session, attempt, lease, server, plugin version, profile, or capability does not match the active job -- **THEN** Platform MUST reject the request without returning declarations, bindings, host targets, or plan data - -### Requirement: Dependency probes and installs use closed typed adapters -Run SHALL execute only supported declared probe kinds and install step types through fixed adapters and SHALL never evaluate arbitrary shell, script text, environment overrides, or caller-supplied command vectors. - -#### Scenario: Declared probe executes -- **WHEN** a supported command-version, Java, Docker, package, service, Steam app, or file probe is requested for the current Run platform -- **THEN** Run MUST resolve only the approved target, enforce timeout/output bounds, and return a safe present/missing/version classification - -#### Scenario: Typed package plan executes -- **WHEN** an approved package step names a supported manager, safe package token, optional safe version, and matching platform -- **THEN** Run MUST use the fixed manager adapter, respect cancellation and timeout, persist step completion idempotently, and never invoke a shell or unapproved privilege escalation - -#### Scenario: Verified download executes -- **WHEN** an approved verified-download step uses HTTPS, an allowed host, a SHA-256 checksum, a bounded size, and a scoped logical destination -- **THEN** Run MUST stream to an owner-only temporary file, verify checksum before atomic publication, and remove invalid partial data - -#### Scenario: Unsafe or unsupported step is requested -- **WHEN** a declaration contains shell syntax, an unsafe package/version token, HTTP or credential-bearing URL, missing checksum, undeclared target, unsupported platform/manager/type, symlink escape, or manual-only step -- **THEN** validation or Run MUST reject it without machine mutation and return a bounded safe failure - -### Requirement: Dependency execution is durable and auditable -Platform and Run SHALL make dependency execution restart-safe, idempotent, cancellable, retry-bounded, and auditable. - -#### Scenario: Run restarts during a multi-step install -- **WHEN** Run recovers an active attempt with a matching immutable digest -- **THEN** it MUST resume after the last durably completed idempotent step and MUST NOT repeat a completed step or accept a stale attempt - -#### Scenario: Cancellation arrives during a blocked adapter -- **WHEN** Platform records cancellation for the active dependency job -- **THEN** Run MUST cancel the adapter context, stop before the next step, preserve recoverable evidence, and report a fenced cancelled result - -#### Scenario: Terminal dependency evidence is accepted -- **WHEN** Platform accepts a current terminal probe or install result -- **THEN** it MUST update the durable dependency status and audit actor, server, plugin, probe/plan, attempt outcome, and safe summary without private execution details - -### Requirement: Dependency work preserves channel deadlines -Slow package managers and downloads SHALL NOT block Run control heartbeat, job acknowledgement/result, cancellation polling, log upload, or artifact channel progress. - -#### Scenario: Dependency adapter is blocked -- **WHEN** a dependency command or download remains blocked beyond a heartbeat interval -- **THEN** heartbeat, log acknowledgement, cancellation polling, and unrelated job-channel requests MUST continue through independent bounded operations diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/run-distribution-and-client-managers/spec.md b/openspec/changes/implement-dependency-installation-and-run-self-update/specs/run-distribution-and-client-managers/spec.md deleted file mode 100644 index 610fcd2..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/run-distribution-and-client-managers/spec.md +++ /dev/null @@ -1,31 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Dependency checks and installs are typed -Run SHALL check dependencies through plugin-declared probes and SHALL install missing dependencies only through approved, typed, reviewable, immutable plans executed by fixed adapters. - -#### Scenario: Dependency check reports missing runtime -- **WHEN** Run evaluates a declared probe for a required runtime, service, package, toolchain, Steam app, Java runtime, Docker runtime, or file and finds it missing -- **THEN** Platform MUST persist and show the safe dependency status and a reviewable platform-matched install plan when the installed plugin declares one - -#### Scenario: Dependency install is approved -- **WHEN** an authorized operator approves the current immutable plan digest -- **THEN** Platform MUST queue a fenced job and Run MUST execute only the typed package, verified-download, or SteamCMD steps, persist resumable evidence, and reject arbitrary shell or stale plan input - -#### Scenario: Dependency result is synthetic -- **WHEN** Run has not executed and verified the declared probe or install steps -- **THEN** it MUST NOT report the dependency present, installed, or successfully completed - -### Requirement: Online run endpoints self-update through platform jobs -The platform SHALL update online Run endpoints through a bounded job that reads an approved same-server target-matched distribution, and Run SHALL durably download, verify, stage, activate, health-check, and roll back the update without receiving raw shell commands. - -#### Scenario: Online Run accepts update -- **WHEN** the assigned endpoint is online, advertises real self-update capability, and the artifact matches its server and OS/architecture -- **THEN** Platform MUST queue a fenced update job and Run MUST download by bounded ranges, verify checksum, stage safely, report the result, and activate only after Platform accepts that result - -#### Scenario: Update verification fails -- **WHEN** Run cannot verify or stage the artifact -- **THEN** Run MUST keep the current executable and configuration, report a bounded failure, preserve heartbeat/status, and never launch the update helper - -#### Scenario: Updated Run fails health confirmation -- **WHEN** the replacement cannot start or authenticate/reconcile with the same identity before timeout -- **THEN** Run MUST restore and restart the previous executable and Platform MUST project a rolled-back/failed outcome rather than success diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/transactional-run-self-update/spec.md b/openspec/changes/implement-dependency-installation-and-run-self-update/specs/transactional-run-self-update/spec.md deleted file mode 100644 index 11530ee..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/specs/transactional-run-self-update/spec.md +++ /dev/null @@ -1,74 +0,0 @@ -## ADDED Requirements - -### Requirement: Run updates use approved target-matched distributions -Platform SHALL dispatch self-update only for an available Run distribution owned by the same server, built for the registered endpoint OS/architecture, and matching the recorded artifact checksum. - -#### Scenario: Authorized update is queued -- **WHEN** an authorized owner or platform administrator selects an available same-server distribution for the online endpoint -- **THEN** Platform MUST bind the update record and job to the distribution, artifact, checksum, target, endpoint, and idempotency key and record a queued audit event - -#### Scenario: Artifact is cross-owner or cross-target -- **WHEN** the artifact belongs to another server/job, is not an available Run distribution, has a different checksum, or targets another OS/architecture -- **THEN** Platform MUST reject the update before job creation without revealing artifact contents or private ownership metadata - -### Requirement: Update artifact reads are resumable and fenced -Run SHALL download update artifacts through a signed active-attempt-only chunk contract with bounded offsets, lengths, total size, and checksum metadata. - -#### Scenario: Download resumes after interruption -- **WHEN** Run restarts or a chunk request fails after a durable offset was recorded -- **THEN** Run MUST request the next bounded range, verify every returned offset/length and the final checksum, and MUST NOT redownload already verified bytes - -#### Scenario: Stale attempt requests a chunk -- **WHEN** a cancelled, expired, wrong-endpoint, wrong-session, wrong-lease, or superseded attempt requests update metadata or bytes -- **THEN** Platform MUST reject it and MUST NOT return artifact bytes, storage paths, browser tokens, secret refs, or fencing hashes - -### Requirement: Run stages updates safely -Run SHALL safely validate and stage exactly the expected Run executable from the approved distribution while preserving the installed package configuration. - -#### Scenario: Valid package is staged -- **WHEN** all artifact bytes and the archive checksum are verified -- **THEN** Run MUST reject archive traversal/links/devices/duplicates, enforce entry and binary size limits, extract the target-matched executable into an owner-only transaction directory, verify its checksum, fsync the journal, and leave the current executable/configuration unchanged - -#### Scenario: Package verification fails -- **WHEN** checksum, target, format, entry bounds, executable identity, or extraction validation fails -- **THEN** Run MUST keep the current executable, remove or quarantine invalid partial data, report a bounded failure, and remain able to heartbeat and accept cancellation - -### Requirement: Activation occurs only after fenced result acceptance -Run SHALL activate a staged update only after Platform accepts the terminal staged result for the current attempt and the local result acknowledgement is durable. - -#### Scenario: Staging result is rejected -- **WHEN** Platform rejects the result because the session, attempt, lease, cancellation state, or terminal fingerprint is stale -- **THEN** Run MUST NOT launch the update helper or replace the executable - -#### Scenario: Staging result is accepted -- **WHEN** Platform accepts the current staged result -- **THEN** Run MUST persist the post-ack activation request, launch the staged helper, stop the old worker without dropping the accepted result, and project the update as restart-requested/activating until health is confirmed - -### Requirement: Activation is health-checked and rollback-safe -The update helper SHALL back up, replace, launch, confirm, and finalize an update transaction, and SHALL restore the previous executable if activation fails. - -#### Scenario: New Run becomes healthy -- **WHEN** the new executable starts, authenticates, registers the same endpoint/server identity, reconciles jobs, and writes the transaction health marker before timeout -- **THEN** it MUST submit a signed current-session health report fenced to the terminal update job/attempt/lease, the helper MUST mark the transaction succeeded, retain bounded rollback evidence, and Platform MUST confirm the endpoint's new release/checksum in the safe update projection only after accepting that report - -#### Scenario: Replacement or health confirmation fails -- **WHEN** copy/rename/start fails, the new process exits, identity differs, or health is not confirmed before timeout -- **THEN** the helper MUST atomically restore the backup where possible, restart the previous executable, mark rolled-back/failed recovery state, and never claim update success - -#### Scenario: Run restarts with an interrupted transaction -- **WHEN** startup finds a durable downloading, staged, activating, or rollback transaction -- **THEN** it MUST resume the safe phase, clean invalid state, or roll back deterministically without applying a different artifact or stale attempt - -### Requirement: Update status and audit projections are safe -Platform_web and plugins SHALL receive only bounded update identity, artifact checksum, target, phase, progress, timestamps, rollback outcome, endpoint version/release, and safe audit summaries. - -#### Scenario: Update status is queried -- **WHEN** an authorized user opens server runtime status -- **THEN** the response MUST omit host/executable/staging/backup paths, raw artifact bodies, credentials, Run tokens, session/lease values or hashes, secret refs, helper PIDs, sockets, and private package configuration - -### Requirement: Update transfer and activation preserve channel deadlines -Slow update downloads and helper preparation SHALL NOT block control heartbeat, job acknowledgement/result, cancellation polling, logs, or unrelated artifact uploads. - -#### Scenario: Update download is slow -- **WHEN** update chunk transfer is delayed or the artifact is large -- **THEN** heartbeat, active job lease renewal, cancellation polling, log upload, and unrelated result reporting MUST continue through separate bounded loops diff --git a/openspec/changes/implement-dependency-installation-and-run-self-update/tasks.md b/openspec/changes/implement-dependency-installation-and-run-self-update/tasks.md deleted file mode 100644 index 33d9a01..0000000 --- a/openspec/changes/implement-dependency-installation-and-run-self-update/tasks.md +++ /dev/null @@ -1,45 +0,0 @@ -## 1. Contracts And Persistence - -- [x] 1.1 Add Platform domain, DTO, model, repository, protocol, validator, and safe projection contracts for dependency catalogs/snapshots/results and Run update manifests/chunks/phases. -- [x] 1.2 Persist Run endpoint OS/architecture, dependency execution evidence, plan digests, and update transaction/rollback status through memory, file, and MySQL snapshot repositories. -- [x] 1.3 Add independent Run protocol/runtime/config/shared types for private dependency input, resumable update reads, typed evidence, and durable update journals without importing main-repository source. - -## 2. Platform Authorization And Orchestration - -- [x] 2.1 Implement authorized dependency catalog/status queries with deterministic safe plan digests and exact installed-plugin/profile/target/binding projections. -- [x] 2.2 Require current plan-digest approval for installs and dispatch only declared platform-matched probes/plans to the selected online endpoint. -- [x] 2.3 Implement signed, session/attempt/lease/cancel-fenced Run dependency-input, update-input, and bounded update-chunk routes with same-server distribution and target checks. -- [x] 2.4 Project accepted terminal dependency/update evidence into durable status/phase/audit records while rejecting stale, cross-owner, cross-endpoint, cross-target, or conflicting results. - -## 3. Real Dependency Execution - -- [x] 3.1 Implement Run probe adapters for supported declared command/version, Java/Docker/package/service/Steam/file checks with bounded redacted evidence. -- [x] 3.2 Implement fixed package-manager, verified HTTPS download, and SteamCMD install adapters with no shell, safe tokens/hosts/checksums/paths, timeouts, and cancellation. -- [x] 3.3 Add durable dependency journals, step idempotency, retry/restart recovery, digest fencing, safe failures for manual/unsupported steps, and tests. - -## 4. Transactional Run Self-Update - -- [x] 4.1 Implement bounded resumable update download with durable offsets, per-range/final checksum verification, cancellation, and restart recovery. -- [x] 4.2 Implement safe zip/tar extraction of the expected target binary, archive/binary limits, owner-only staging, configuration preservation, and durable transaction manifests. -- [x] 4.3 Implement post-result-ack helper activation, parent exit coordination, backup/atomic replacement, helper-environment cleanup, startup identity/health confirmation, rollback, and interrupted-transaction recovery. -- [x] 4.4 Add Run self-update tests for wrong artifact/target/checksum, partial resume, stale fencing, result rejection, activation success, health timeout rollback, and journal restart. - -## 5. Plugins And Platform Web - -- [x] 5.1 Tighten plugin manifest/SDK dependency declarations and example plans for fixed adapters, approved download hosts/checksums, step bounds, and unsafe shell/URL/token rejection. -- [x] 5.2 Add platform_web safe dependency catalog/status and Run update phase/checksum/rollback/audit views using existing black-mecha/magical-girl components and existing 401/403 behavior. - -## 6. Regression And Verification - -- [x] 6.1 Add Platform/Run regression coverage for owner/endpoint/signature/session/attempt/lease/target rejection, persistence/recovery/idempotency/cancel, redaction, and blocked transfer/adapter channel isolation. -- [x] 6.2 Update protocol/API/domain documentation with implemented limits and explicitly excluded client-manager lifecycle, production signing/fleet rollout/scaling/alerts/plugin lifecycle, and real AI-provider integration. -- [x] 6.3 Run plugin manifest/SDK tests, Platform tests, platform_web tests/typecheck/build, independent Run tests, strict OpenSpec validation, structure, shell/compose checks, and both repository diff checks; record only passing evidence. - -## Verification Evidence - -- `plugins`: `npm run validate:manifest`, `npm run typecheck`, and `npm test` passed (18 tests). -- `platform`: `go test ./...` passed across api/config/domain/dto/model/repo/service/validator. -- `platform_web`: `npm test` passed (104 tests), `npm run typecheck`, and `npm run build` passed. -- independent `run`: `go test ./...` passed across api/config/protocol/runtime/spool. -- `openspec validate implement-dependency-installation-and-run-self-update --strict` passed. -- `scripts/check-structure.sh`, `bash -n scripts/*.sh`, `docker compose config`, `git diff --check`, and `git -C run diff --check` passed. diff --git a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/.openspec.yaml b/openspec/changes/implement-durable-job-scheduling-and-reconciliation/.openspec.yaml deleted file mode 100644 index ff5f854..0000000 --- a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-17 diff --git a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/design.md b/openspec/changes/implement-durable-job-scheduling-and-reconciliation/design.md deleted file mode 100644 index dd96110..0000000 --- a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/design.md +++ /dev/null @@ -1,78 +0,0 @@ -## Context - -`domain.Job` is already the durable unit stored by MemoryStore, FileStore, and the repository-backed MySQLStore, but it currently contains only the user-visible lifecycle projection. `CoreService` separately owns an in-memory `jobLeases` map containing raw Run session tokens, lease tokens, attempts, cancel intent, and terminal fingerprints. Restarting the platform therefore makes accepted/running jobs impossible to authenticate or complete safely. The independent Run repository similarly uses an in-memory active assignment map and does not reconcile during startup. - -The existing HTTP surface already separates control, jobs, logs, and artifacts and signs non-hello Run requests with the active Run session. This change must deepen those boundaries without introducing an all-in-one transport, exposing credentials to the browser/plugins, or implementing later execution/storage roadmap items. - -## Goals / Non-Goals - -**Goals:** - -- Make every scheduling decision recoverable from the Job repository after platform process restart or FileStore/MySQLStore reload. -- Fence every ack, progress, result, cancel poll, and reconcile report by endpoint, active authenticated session, per-job attempt, and a lease credential whose persisted form is a hash. -- Define bounded ack and execution leases, monotonic per-job attempts and progress sequences, exponential retry backoff, terminal idempotency, and durable cancel intent/result. -- Make Run persist active assignments atomically under its workspace, reconcile immediately after registration, and recover or discard work according to the platform response. -- Keep safe browser projections and owner/platform-admin authorization aligned with existing server ownership checks. -- Preserve independent control/job/log/artifact paths and prove large transfer requests cannot block control or job requests. - -**Non-Goals:** - -- Production-grade distributed scheduling or multi-platform-replica compare-and-swap coordination. -- Process supervision, real config/file mutation, durable logs/artifacts/metrics/backups, dependency installation, Run self-update, client-manager lifecycle, or production scaling. -- Persisting raw Run session tokens, lease tokens, host paths, sockets, AI keys, component keys, or other credentials. - -## Decisions - -### Persist scheduling metadata on the Job aggregate - -`domain.Job` and `model.Job` gain a nested retry policy plus queue, attempt, hashed lease, ack/lease deadline, last progress sequence, cancel, terminal, and reconciliation fields. The existing Job repository remains the only storage boundary, so MemoryStore, FileStore, and MySQLStore inherit the behavior through their existing typed snapshot/repository implementations. - -This is preferred over retaining a service cache or introducing a second lease repository because transitions need one recoverable aggregate and the current stores do not provide cross-repository transactions. Raw lease tokens are generated with cryptographic randomness, returned only over the signed Run job channel, and stored as SHA-256 hashes. - -### Use a deterministic per-job state machine - -New jobs default to `queued`, attempt zero, a bounded retry policy, and an immediately eligible queue timestamp. Claim sweeps expired work for the endpoint, selects eligible `queued` or `retrying` work in stable creation order, increments that job's attempt, stores the lease hash and current Run session generation, and moves it to `accepted` with an ack deadline and execution lease deadline. - -Ack before the deadline moves the attempt to `running` and renews its lease. Monotonic progress renews the running lease. Ack timeout, execution lease expiry, or an explicitly retryable failed result clears the lease and moves the job to `retrying` with exponential backoff when attempts remain; otherwise it records a terminal `failed` result. A pending cancel intent always resolves to `cancelled` rather than retrying when the lease expires. Old attempts and old lease tokens are rejected even after a newer attempt exists. - -### Model cancellation as durable intent followed by durable result - -Cancel before claim atomically records intent and a terminal cancelled result. Cancel after claim records idempotent intent for the assigned Run endpoint; polling is fenced by the current attempt and lease, and Run returns a normal terminal cancelled result. Repeated identical or compatible cancel requests return the existing intent/result projection. Cancellation never consumes another retry attempt. - -### Reconcile with full active-attempt evidence - -Run reports persisted active assignment evidence: job ID, attempt, and raw lease token over the signed job channel. Platform verifies the endpoint, stored attempt, lease hash, nonterminal state, and active Run session, then rebinds the lease to the current session generation and renews its deadline. The raw token is echoed only in the signed response and is never persisted. - -Reported stale/unknown jobs are returned as discard IDs. Platform-active jobs absent from the Run journal are treated as abandoned and enter cancel resolution or retry policy. Reconciliation timestamps, counts, and outcome are persisted on every affected Job. This is preferred over recreating leases during reconciliation because recreating them would let stale attempts regain authority. - -### Persist Run journal atomically and reconcile before claiming - -Run stores a versioned JSON journal under `WorkspaceRoot`, using a temporary file plus rename and owner-only permissions. It writes the assignment before ack, updates it after ack/progress, and removes it only after an accepted terminal response or explicit platform discard. Worker startup registers, reconciles the loaded journal before any new claim, and re-executes platform-confirmed assignments under the same attempt. Corrupt journal data fails worker construction instead of silently forgetting active work. - -### Keep API and UI projections credential-free - -Platform user APIs expose attempt, max attempts, next retry time, ack/lease deadlines, cancel state/timestamps, terminal time, and last reconcile outcome. They omit lease hashes, raw lease tokens, session generation, secret refs, and host/runtime credentials. Existing `GetJobForSession`, `ListJobsForSession`, and cancel authorization continue to derive access from server owner/admin or platform-admin rules. Run-only handlers continue to require both the active session and request signature middleware. - -### Preserve channel independence - -Control, jobs, logs, and artifacts remain separate HTTP routes and clients. Run's worker loop executes heartbeat and scheduling work independently from spool/artifact transfer queues; neither the job protocol nor safe browser projection carries log bodies or artifact payloads. Isolation tests block log/artifact handlers while asserting heartbeat, ack, result, cancel, and reconcile complete. - -## Risks / Trade-offs - -- [FileStore and the current MySQLStore are process-local snapshot implementations, not distributed CAS schedulers] -> Serialize transitions with the existing service mutex and explicitly keep multi-replica production scheduling out of scope. -- [At-least-once recovery can repeat an interrupted operation] -> Preserve idempotency keys, fence attempts, and require bounded Run executors to be idempotent; later process supervision will refine resumability. -- [Legacy persisted jobs lack new fields] -> Normalize zero-value scheduling fields when read/claimed so existing queued and terminal records remain valid without destructive migration. -- [Hash-only lease storage means platform cannot recreate a lost Run lease credential] -> Require the durable Run journal to present the original token; otherwise the platform retries with a new attempt after reconciliation/expiry. -- [Clock skew between Run and platform] -> Treat platform timestamps as authoritative; Run does not decide lease validity locally. - -## Migration Plan - -1. Deploy the expanded model/repository projection and zero-value normalization before relying on new states. -2. Deploy platform protocol and scheduler behavior with compatibility for an empty legacy reconcile evidence list. -3. Deploy Run protocol and persistent journal, which reconciles immediately after registration before claiming. -4. Deploy the safe web projection and tests. -5. Rollback may read the expanded JSON while ignoring unknown fields, but accepted/running jobs should be allowed to reconcile or expire before rolling back to code that lacks durable fencing. - -## Open Questions - -None for this single-process scheduling milestone. Cross-replica transactional claiming and resumable supervised processes remain explicit later design work. diff --git a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/proposal.md b/openspec/changes/implement-durable-job-scheduling-and-reconciliation/proposal.md deleted file mode 100644 index e179e71..0000000 --- a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/proposal.md +++ /dev/null @@ -1,32 +0,0 @@ -## Why - -The platform currently persists the visible Job record but keeps leases, attempts, cancellation intent, and terminal idempotency in `CoreService` memory, so a platform restart loses fencing and recovery state. Run also keeps its active-job journal only in memory, preventing reliable reconciliation after Run restart and leaving ack timeouts, retry backoff, and late-result rejection underspecified. - -## What Changes - -- Persist queue scheduling, attempt, lease hash and deadlines, retry/backoff, terminal fingerprint, cancellation intent/result, progress sequence, and reconciliation metadata through the existing MemoryStore, FileStore, and MySQLStore Job repository boundary. -- Define claim, ack, lease renewal, retry-wait, terminal, cancel, and reconciliation transitions with attempt fencing and deterministic late-message rejection. -- Rebind a valid persisted lease to a newly authenticated Run session generation only through endpoint-scoped reconciliation; never persist raw Run session or lease credentials. -- Persist Run's active assignment journal locally, reconcile it after registration and restart, continue valid attempts, and discard platform-rejected or unknown work. -- Keep control, jobs, logs, artifacts, and the optional game-client bridge as independent request paths and execution queues so log/artifact backpressure cannot block heartbeat or job acknowledgement/result traffic. -- Extend platform APIs and the management console only with safe scheduling projections such as state, attempt, retry timing, cancel status, and reconcile status. Raw tokens, secret references, host paths, sockets, and credentials remain excluded. -- Add cross-store, API, frontend, and independent Run regression coverage for reload recovery, deadlines, fencing, retry, cancellation, reconciliation, authorization/signature failure, and channel isolation. -- Explicitly leave process supervision, real config/file execution, durable log/artifact/metric/backup storage, dependency installation, self-update, client-manager lifecycle, and production scaling to later changes. - -## Capabilities - -### New Capabilities - -- `durable-job-scheduling`: Defines durable platform scheduling and Run reconciliation semantics, retry and cancellation state, security fencing, safe projections, and channel isolation. - -### Modified Capabilities - -None. - -## Impact - -- `platform/`: Job domain/model/repository projections, scheduler service, Run job protocol DTOs and validators, owner/admin/Run-service authorization, API handlers, and persistence/reload tests. -- `run/` independent repository: job protocol contracts, persistent journal, worker startup reconciliation, cancellation/result behavior, and isolated HTTP channel tests. -- `platform_web/`: safe Job API types, schemas, task-status presentation, and 401/403 regression coverage without visual-system redesign. -- `plugins/`: existing SDK and manifests are verified to remain platform-mediated; no raw Run credential or host access is added. -- OpenSpec: adds a cross-repository behavioral contract and verification checklist while leaving earlier completed changes unarchived. diff --git a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/specs/durable-job-scheduling/spec.md b/openspec/changes/implement-durable-job-scheduling-and-reconciliation/specs/durable-job-scheduling/spec.md deleted file mode 100644 index 12935d9..0000000 --- a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/specs/durable-job-scheduling/spec.md +++ /dev/null @@ -1,117 +0,0 @@ -## ADDED Requirements - -### Requirement: Durable scheduling state -The platform SHALL persist queue eligibility, retry policy, attempt, lease hash and deadlines, progress sequence, cancellation intent/result, terminal fingerprint, and reconciliation metadata through the Job repository used by MemoryStore, FileStore, and MySQLStore. It MUST NOT require a `CoreService` memory map to recover active scheduling state and MUST NOT persist raw Run session or lease tokens. - -#### Scenario: Platform reload preserves active attempt -- **WHEN** a claimed or running job is reloaded into a new platform service instance -- **THEN** the stored endpoint, attempt, hashed lease, deadlines, cancel intent, and retry metadata remain authoritative and a correctly signed current-session request with the matching lease is accepted - -#### Scenario: File and MySQL reload preserve queued work -- **WHEN** queued or retry-wait work is persisted and the store is reopened -- **THEN** the same job becomes claimable only at its persisted eligibility time with its prior attempt count intact - -### Requirement: Lease and attempt fencing -The platform SHALL issue cryptographically random per-attempt lease tokens, persist only their hashes, and fence job messages by Run endpoint, authenticated active session generation, job ID, monotonic per-job attempt, and matching lease token. Accepted jobs SHALL have an acknowledgement deadline and running jobs SHALL have a renewable execution lease. - -#### Scenario: Ack deadline expires -- **WHEN** Run does not acknowledge a claimed job before its acknowledgement deadline -- **THEN** the platform rejects the late acknowledgement and schedules the job for a later attempt or records terminal failure when retry budget is exhausted - -#### Scenario: Execution lease expires -- **WHEN** a running attempt sends no accepted progress or reconciliation before its lease expires -- **THEN** the platform clears that lease and applies retry or terminal policy durably - -#### Scenario: Old attempt arrives late -- **WHEN** an ack, progress update, result, cancel poll, or reconciliation entry references an older attempt or lease -- **THEN** the platform rejects it without changing the current attempt or terminal result - -#### Scenario: Invalid endpoint or session -- **WHEN** otherwise valid attempt evidence is signed by another endpoint, an expired or rotated session, or an invalid signature -- **THEN** the platform returns an authentication or authorization failure and leaves the job unchanged - -### Requirement: Retry and terminal policy -Each job SHALL have a bounded retry policy with a maximum attempt count and exponential backoff capped by a maximum delay. Ack timeout, lease expiry, and explicitly retryable failure SHALL enter durable `retrying` state when budget remains. Succeeded, non-retryable failed, cancelled, and exhausted jobs SHALL be terminal and terminal replay SHALL be idempotent only for the same attempt and result fingerprint. - -#### Scenario: Retry waits for backoff -- **WHEN** an attempt fails retryably and attempts remain -- **THEN** the job records the next eligible time and cannot be claimed before that time - -#### Scenario: Retry claim increments attempt -- **WHEN** backoff has elapsed and Run claims the job again -- **THEN** the platform increments the per-job attempt and issues a different lease token - -#### Scenario: Retry budget is exhausted -- **WHEN** another retryable failure occurs on the maximum attempt -- **THEN** the job becomes terminal failed and is never returned by claim - -#### Scenario: Terminal replay conflicts -- **WHEN** Run replays the same terminal result fingerprint for the current terminal attempt -- **THEN** the platform returns the existing accepted result, while a different fingerprint or attempt is rejected - -### Requirement: Idempotent durable cancellation -The platform SHALL authorize cancellation through existing owner/server-admin/platform-admin resource checks, persist cancellation intent, and persist its terminal result. Cancellation before claim SHALL complete immediately; cancellation after claim SHALL be delivered only to the fenced active attempt and SHALL resolve idempotently. - -#### Scenario: Cancel before claim -- **WHEN** an authorized user cancels queued or retry-wait work -- **THEN** the job records both cancel intent and terminal cancelled result without being claimed - -#### Scenario: Cancel after claim -- **WHEN** an authorized user cancels accepted or running work -- **THEN** matching Run cancel polling observes the durable intent and a cancelled result records durable completion - -#### Scenario: Cancel is repeated -- **WHEN** the same authorized cancellation is requested or polled more than once -- **THEN** the platform returns the existing intent/result without creating another attempt or conflicting terminal state - -#### Scenario: Cross-owner cancellation is denied -- **WHEN** a non-admin user attempts to cancel a job for a server they do not own or administer -- **THEN** the platform returns forbidden and does not persist cancel intent - -### Requirement: Run restart and platform reconciliation -Run SHALL persist active assignments atomically before acknowledgement and reconcile them immediately after every registration before claiming new work. Reconciliation SHALL report job ID, attempt, and lease evidence; platform SHALL confirm only matching active attempts, rebind them to the current authenticated session generation, persist reconciliation metadata, and direct Run to discard stale or unknown entries. Platform-active entries absent from Run's report SHALL enter cancellation resolution or retry policy. - -#### Scenario: Run restart resumes confirmed attempt -- **WHEN** Run restarts with a valid persisted active assignment and registers a rotated session -- **THEN** reconciliation confirms and rebinds the same attempt before Run resumes it or claims other work - -#### Scenario: Run reports stale journal entry -- **WHEN** Run reports a terminal, unknown, wrong-endpoint, wrong-attempt, or wrong-lease journal entry -- **THEN** the platform does not reactivate it and instructs Run to discard it - -#### Scenario: Platform restart accepts reconciliation -- **WHEN** platform restarts while Run retains a valid active journal entry -- **THEN** the platform validates it against persisted job metadata without relying on prior process memory - -#### Scenario: Platform active work is missing from Run -- **WHEN** authenticated reconciliation omits an accepted or running job assigned to that endpoint -- **THEN** the platform records reconciliation loss and applies cancel or retry policy rather than silently leaving unrecoverable active work - -### Requirement: Credential-free user projection -The platform user API and platform_web SHALL expose only safe scheduling projections, including state, attempt counts, retry timing, cancel status, and reconcile outcome. They MUST NOT expose raw or hashed lease tokens, Run sessions, secret references, host paths, sockets, or credentials, and SHALL preserve existing 401/403 handling and crystal-moonlight console styling. - -#### Scenario: Authorized user reads job scheduling status -- **WHEN** a server owner, server administrator, or platform administrator reads an accessible job -- **THEN** the response includes safe attempt, retry, cancellation, terminal, and reconcile fields without credential material - -#### Scenario: Unauthorized user reads another owner's job -- **WHEN** a user without resource access requests another server's job -- **THEN** the API returns forbidden or not found according to the existing resource policy and platform_web follows existing 401/403 handling - -### Requirement: Independent channel priority -Platform and Run SHALL keep control, jobs, logs, and artifacts on independent request paths and execution queues. Blocking or retrying log/artifact transfer MUST NOT block control heartbeat or job claim, ack, progress, result, cancel, or reconciliation traffic, and job/control messages MUST NOT carry log bodies or artifact payloads. - -#### Scenario: Artifact transfer blocks -- **WHEN** an artifact chunk request remains blocked -- **THEN** heartbeat and job acknowledgement/result/cancel/reconcile requests still complete within their own deadlines - -#### Scenario: Log ingest blocks -- **WHEN** a log batch upload remains blocked or retries -- **THEN** control heartbeat and job lifecycle traffic continue independently - -### Requirement: Explicit roadmap boundary -This change SHALL NOT claim production readiness for process supervision, real config/file execution, durable log/artifact/metric/backup storage, dependency installation, Run self-update, client-manager lifecycle, or production multi-replica scaling. - -#### Scenario: Completion is reported -- **WHEN** the durable scheduling change passes implementation and verification -- **THEN** its handoff identifies those capabilities as remaining later-route work diff --git a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/tasks.md b/openspec/changes/implement-durable-job-scheduling-and-reconciliation/tasks.md deleted file mode 100644 index e4edcd4..0000000 --- a/openspec/changes/implement-durable-job-scheduling-and-reconciliation/tasks.md +++ /dev/null @@ -1,43 +0,0 @@ -## 1. Durable Platform Model - -- [x] 1.1 Add Job domain/model scheduling, retry, lease, cancellation, terminal, and reconciliation fields with legacy zero-value normalization and validation. -- [x] 1.2 Persist and reload the expanded Job aggregate through MemoryStore, FileStore, and MySQLStore repository paths without raw lease or session credentials. - -## 2. Platform Scheduler State Machine - -- [x] 2.1 Replace the CoreService in-memory job lease map with repository-backed claim, ack, lease renewal, progress sequencing, retry backoff, and terminal fencing transitions. -- [x] 2.2 Implement idempotent cancel-before-claim, cancel-after-claim polling/result, deadline expiry, late-message rejection, and exhausted retry behavior. -- [x] 2.3 Implement endpoint/session-generation reconciliation using attempt and lease evidence, including missing/stale work handling and persisted reconcile outcomes. - -## 3. Contracts, Security, And API - -- [x] 3.1 Extend platform Run job DTO/domain/validator/protocol contracts for deadlines, retryable result, full reconciliation evidence, and discard outcomes. -- [x] 3.2 Preserve signed Run-service endpoint/session checks and owner/server-admin/platform-admin authorization for job read and cancellation APIs. -- [x] 3.3 Expose only credential-free user Job scheduling projections and update API contract documentation. - -## 4. Independent Run Recovery - -- [x] 4.1 Extend the independent Run job protocol and client for deadlines, retry outcomes, reconciliation evidence, and discard instructions. -- [x] 4.2 Implement an atomic, owner-only persistent Run job journal and startup reconciliation before new claims. -- [x] 4.3 Make the worker recover confirmed attempts, handle cancellation idempotently, retain unaccepted results for later reconciliation, and discard platform-rejected entries. -- [x] 4.4 Prove control/job requests remain independent while log or artifact transfers block. - -## 5. Safe Console Projection - -- [x] 5.1 Extend platform_web API types and schemas with safe attempt, retry, cancel, terminal, and reconcile fields while preserving shared visual styles and 401/403 behavior. -- [x] 5.2 Update runtime task status presentation and tests without exposing lease/session/secret/host data or redesigning the console. - -## 6. Regression And Completion Evidence - -- [x] 6.1 Add platform tests for queue/store reload, lease and ack expiry, attempt fencing, retry/backoff, cancellation timing/idempotency, late messages, restart reconciliation, endpoint/owner rejection, and signature/session failure. -- [x] 6.2 Add independent Run tests for journal reload, restart reconciliation/recovery, cancel/result retention, stale discard, and channel isolation. -- [x] 6.3 Run plugin manifest/SDK tests, platform Go tests, platform_web tests/typecheck/build, independent Run tests, strict OpenSpec validation, structure check, shell/compose checks where affected, and both repositories' diff checks; then record evidence before checking tasks complete. - -## Verification Evidence (2026-07-18) - -- `platform`: `go test -count=1 ./...` passed across API, config, domain, DTO, model, repository, service, and validator packages. -- `platform_web`: 92 tests passed across 17 files; `npm run typecheck` and production `npm run build` passed. -- `plugins`: 17 manifest/SDK tests passed; TypeScript typecheck passed; dev, SCUM, and Minecraft example manifests validated. -- Independent `run`: `go test -count=1 ./...` passed across API, config, protocol, runtime, and spool packages. -- `openspec validate implement-durable-job-scheduling-and-reconciliation --strict`, `scripts/check-structure.sh`, shell syntax checks, and `docker compose config --quiet` passed. -- `git diff --check` passed in both `/Users/tasia/Desktop/code/browser` and the independent `/Users/tasia/Desktop/code/browser/run` repositories. diff --git a/openspec/changes/implement-durable-observability-and-remote-adapters/design.md b/openspec/changes/implement-durable-observability-and-remote-adapters/design.md deleted file mode 100644 index 320b5b6..0000000 --- a/openspec/changes/implement-durable-observability-and-remote-adapters/design.md +++ /dev/null @@ -1,53 +0,0 @@ -## Context - -Earlier changes introduced channelized log ingest and artifact transfer contracts, but the Platform service keeps log batches, artifact transfer state, and artifact payload bytes in process memory. Metrics are currently generated from a live snapshot, backups are not represented, and Run remote access execution is a success-only placeholder. The repository already has file and MySQL metadata snapshots, signed Run requests, endpoint/session fencing, scoped plugin permissions, and safe frontend projections. This change extends those boundaries without replacing them. - -## Goals - -- Make restart behavior explicit and testable for logs, artifacts, metrics, and backups. -- Keep large bodies outside lightweight job/control payloads and expose only bounded, redacted projections to users, plugins, and platform_web. -- Make remote adapters declaration-driven and auditable, with cancellation and lease/attempt fencing handled by the existing job channel. -- Preserve independent channel priorities so slow artifact or adapter work cannot delay control heartbeat, job ack/result, or log acknowledgement. - -## Decisions - -### Decision 1: File-backed durable bodies behind service interfaces - -Log segments, artifact chunks, and completed artifact content use owner-only files under configured private roots. Metadata and cursors remain in the existing repository snapshot (file or MySQL). Atomic temp-file rename, bounded reads, checksum verification, and startup reconstruction keep writes recoverable. Memory stores remain available for unit tests. - -### Decision 2: Transfer manifests are the recovery source of truth - -Artifact transfer sessions persist an idempotency key, owner scope, direction, size/chunk limits, received indexes, and final checksum. A restart reloads incomplete manifests and reconstructs next-missing state without exposing payload paths. A chunk is removed from the Run queue only after an acknowledgement for the exact transfer/artifact/index. - -### Decision 3: Retention is bounded and deterministic - -Log streams carry bounded retention count/age metadata; metric samples and backup records have configurable maximum records/bytes and oldest-first pruning. Pruning emits an audit record and never changes an acknowledged log sequence or an available artifact checksum. Recovery marks interrupted backups/receipts as recoverable failure rather than claiming success. - -### Decision 4: Metrics and backups are append-only records with safe projections - -Metric samples store server/run identity, timestamp, bounded numeric values, and source. Backup records store logical scope, artifact reference, checksum, size, state, and recovery/audit status. Host paths, credentials, sockets, PID, session/lease tokens, secret refs, and hashes used for fencing are excluded from response DTOs. - -### Decision 5: Remote adapters are a constrained registry, not a command tunnel - -The Platform registers adapter declarations from an installed plugin/runtime profile and authorizes a request only when owner/admin scope, server instance, selected endpoint, declared capability, and target allowlist all match. Run accepts a typed adapter kind and logical target key, validates timeout and retry bounds, checks context cancellation before and during work, and returns a safe result reference. Shell source, arbitrary command vectors, raw socket addresses, unapproved hosts, and embedded credentials are rejected. Existing Job attempt/lease/session fencing remains authoritative. - -### Decision 6: Lightweight routes stay isolated - -Control and job routes continue to reject log/artifact payload fields. Log ingest and artifact transfer use separate clients/queues. Remote adapter work is scheduled as a job and its result is metadata-only; it cannot write through control or log endpoints. Tests exercise interleaving and blocked/slow operations with bounded deadlines. - -## Data Flow - -1. Run appends a bounded log batch or artifact chunk to its owner-only spool/queue before upload. -2. A low-priority uploader sends the batch/chunk on its dedicated route; the Platform validates signature, endpoint/session, owner scope, sequence/range/checksum, and idempotency before durable append. -3. Platform persists metadata and body/manifests atomically, returns an acknowledgement, and the Run removes only the acknowledged item. -4. Metrics and backup records are written through bounded service methods, pruned deterministically, and queried through authorized safe DTOs. -5. A declared remote adapter request becomes a fenced job. Run executes only the typed adapter implementation, returns a bounded status/result ref, and Platform audits/project results after terminal fencing. - -## Non-Goals And Follow-Ups - -- No dependency installation, Run self-update, client-manager lifecycle, plugin lifecycle, production scaling/alerts, external object stores, arbitrary FTP/rsync/DB/RCON network access, or real AI provider integration. -- Native OS process birth tokens and distributed transaction guarantees remain outside this change. - -## Rollback - -The additive protocol and repository fields are backward compatible. Removing the new capabilities stops advertising durable/adapter features and leaves old metadata untouched; incomplete transfer manifests remain private and can be retried by a compatible release. diff --git a/openspec/changes/implement-durable-observability-and-remote-adapters/proposal.md b/openspec/changes/implement-durable-observability-and-remote-adapters/proposal.md deleted file mode 100644 index 4450aee..0000000 --- a/openspec/changes/implement-durable-observability-and-remote-adapters/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -The platform has typed log and artifact routes, but restart recovery is incomplete: log acknowledgements and artifact transfer state are not durable, metrics are derived on demand, and backups and restricted remote adapters have no durable ownership/audit model. This change makes those existing channels operationally durable while preserving the control/job/log/artifact priority boundaries established by earlier changes. - -## What Changes - -- Persist log stream batches, acknowledgement cursors, retention metadata, and bounded queries across Platform restarts; keep Run local spool files retryable and recoverable. -- Persist artifact metadata, transfer sessions, chunk manifests, checksums, and content through a restart-safe bounded file-backed store; keep chunk retries idempotent and lower priority than logs/jobs/control. -- Add bounded metrics samples and backup records with retention, size limits, recovery status, and audit events; expose only safe projections. -- Add declaration-backed remote adapter requests for approved FTP/rsync/run-file/process/database/RCON operations with scoped targets, timeout/cancel/retry/fencing, and audit outcomes. No arbitrary shell, raw socket, unapproved host/credential, or bypass of Platform ownership/endpoint/session checks. -- Add Platform and Run protocol/client/runtime contracts plus platform_web safe status projections and regression coverage. - -## Capabilities - -### New Capabilities - -- `durable-observability`: durable logs, artifacts, metrics, backups, retention, recovery, and safe query projections. -- `scoped-remote-adapters`: declared and authorized remote adapter execution with bounded lifecycle and audit semantics. - -### Modified Capabilities - -- `log-ingest-pipeline`: durable acknowledgement and restart recovery replace the earlier in-memory service assumption. -- `artifact-transfer-channel`: transfer manifests and content survive restart and retain idempotent chunk/checksum behavior. - -## Impact - -- Affects `platform/`, independent `run/`, and projection-only `platform_web/` contracts/views. -- Adds dedicated domain, DTO, model, repository, service, protocol, validator, and runtime types; no Run source is copied into the main repository. -- Does not implement dependency installation, Run self-update, client-manager lifecycle, plugin lifecycle, production scaling/alerts, or real third-party AI provider integration. diff --git a/openspec/changes/implement-durable-observability-and-remote-adapters/specs/durable-observability/spec.md b/openspec/changes/implement-durable-observability-and-remote-adapters/specs/durable-observability/spec.md deleted file mode 100644 index bc194d1..0000000 --- a/openspec/changes/implement-durable-observability-and-remote-adapters/specs/durable-observability/spec.md +++ /dev/null @@ -1,61 +0,0 @@ -## ADDED Requirements - -### Requirement: Durable logs recover after restart - -The system SHALL persist accepted log batches, acknowledged sequence state, retention metadata, and bounded query indexes so a Platform restart does not duplicate or lose acknowledged ranges. - -#### Scenario: Restart preserves log cursor - -- **WHEN** a batch is acknowledged, Platform restarts, and a caller queries after a cursor -- **THEN** the ordered entries and latest acknowledged sequence MUST be available from the persisted store - -#### Scenario: Retry remains idempotent - -- **WHEN** Run retries an acknowledged batch with the same stream, range, and checksum -- **THEN** Platform MUST return an idempotent acknowledgement without duplicating entries - -### Requirement: Run log spool is restart-safe - -The Run log spool SHALL atomically persist unacknowledged batches, tolerate a process restart, and remove a batch only when an acknowledgement covers its full stream range. - -#### Scenario: Interrupted enqueue - -- **WHEN** a process restarts after an incomplete temporary spool write -- **THEN** the next spool load MUST ignore temporary files and retain every committed unacknowledged batch - -### Requirement: Durable artifacts recover with checksum and chunk bounds - -The system SHALL persist artifact metadata, transfer manifests, received chunk indexes, chunk checksums, final checksums, and bounded content so uploads can resume after restart. - -#### Scenario: Resume missing chunk - -- **WHEN** a transfer has received some chunks and Platform restarts -- **THEN** status MUST return the same received indexes and next missing index without exposing storage paths - -#### Scenario: Checksum conflict is rejected - -- **WHEN** a retry uses a different payload or checksum for an already received chunk -- **THEN** Platform MUST reject it and leave the original chunk and transfer state unchanged - -### Requirement: Metrics and backups are durable and bounded - -The system SHALL persist metric samples and backup records, apply explicit age/count/byte retention, support recovery status, and expose only owner-authorized safe projections. - -#### Scenario: Retention prunes oldest records - -- **WHEN** a metric or backup append exceeds its configured bound -- **THEN** the oldest records MUST be pruned deterministically and an audit event MUST record the retention result - -#### Scenario: Interrupted backup is recoverable - -- **WHEN** a backup remains in an incomplete state during restart -- **THEN** it MUST be projected as failed/recoverable with an audit outcome and MUST NOT claim an available artifact - -### Requirement: Safe queries enforce ownership - -The system SHALL authorize log, artifact, metric, and backup queries by platform session and server ownership/admin scope, returning bounded pages/cursors and never returning host paths, credentials, sockets, Run tokens, leases, session hashes, or secret references. - -#### Scenario: Cross-owner query - -- **WHEN** a user queries another owner's resource -- **THEN** the service MUST reject with the existing 403 behavior and MUST NOT reveal whether private body data exists diff --git a/openspec/changes/implement-durable-observability-and-remote-adapters/specs/scoped-remote-adapters/spec.md b/openspec/changes/implement-durable-observability-and-remote-adapters/specs/scoped-remote-adapters/spec.md deleted file mode 100644 index aed5f00..0000000 --- a/openspec/changes/implement-durable-observability-and-remote-adapters/specs/scoped-remote-adapters/spec.md +++ /dev/null @@ -1,47 +0,0 @@ -## ADDED Requirements - -### Requirement: Remote adapters are declared and scoped - -The system SHALL accept only typed adapter kinds and logical target keys declared by the installed plugin/runtime profile and selected Run endpoint. - -#### Scenario: Undeclared adapter - -- **WHEN** a request names an adapter or target not declared for the server and endpoint -- **THEN** Platform MUST reject it before creating a job - -#### Scenario: Unsafe target data - -- **WHEN** a request contains shell source, raw socket addresses, host paths, credentials, or unbounded inline query/command data -- **THEN** validation MUST reject it and MUST NOT persist the unsafe fields - -### Requirement: Adapter lifecycle is bounded and fenced - -The system SHALL enforce timeout, cancellation, retry, endpoint/session, attempt, and lease fencing using the existing job channel. - -#### Scenario: Cancelled adapter - -- **WHEN** cancellation arrives before or during adapter execution -- **THEN** Run MUST stop at a bounded checkpoint and return a cancelled safe result; Platform MUST not apply a stale terminal result - -#### Scenario: Stale attempt result - -- **WHEN** an older attempt reports success after a newer attempt owns the lease -- **THEN** Platform MUST reject the result and retain the newer job state - -### Requirement: Adapter results are auditable projections - -The system SHALL persist an audit event for authorization, timeout, cancellation, success, and failure outcomes and expose only adapter kind, target key, status, bounded message, and safe result references. - -#### Scenario: Successful scoped adapter - -- **WHEN** a declared adapter completes within its deadline -- **THEN** the operator MUST see a safe status and audit summary without raw host/credential/socket details - -### Requirement: Channel isolation is maintained - -Remote adapter work and artifact transfer SHALL use lower-priority independent work paths and MUST NOT delay control heartbeat, job ack/result, or log upload acknowledgement beyond their deadlines. - -#### Scenario: Slow adapter and artifact transfer - -- **WHEN** adapter or chunk work blocks or retries -- **THEN** control, job lifecycle, and log acknowledgement calls MUST remain independently completable diff --git a/openspec/changes/implement-durable-observability-and-remote-adapters/tasks.md b/openspec/changes/implement-durable-observability-and-remote-adapters/tasks.md deleted file mode 100644 index 4880cb4..0000000 --- a/openspec/changes/implement-durable-observability-and-remote-adapters/tasks.md +++ /dev/null @@ -1,29 +0,0 @@ -## 1. OpenSpec And Contracts - -- [x] 1.1 Add durable log/artifact/metrics/backup/remote adapter domain, DTO, model, protocol, and validator contracts with forbidden-field tests. -- [x] 1.2 Extend file/MySQL snapshot and repository interfaces for durable bodies, transfer manifests, samples, backups, and audit projections. -- [x] 1.3 Update route/protocol/API contracts and platform_web safe types without exposing Run tokens, lease/session hashes, host paths, credentials, sockets, PID, or secret refs. - -## 2. Durable Logs And Artifacts - -- [x] 2.1 Make Platform log batches, cursors, retention, and file-backed body storage restart-safe and queryable with idempotent ack/retry. -- [x] 2.2 Make artifact transfer sessions/chunks/content durable, bounded, resumable, checksum-verified, and idempotent across restart. -- [x] 2.3 Make Run spool/queues atomic and restart-safe, and keep log/artifact upload scheduling independent from control/jobs. - -## 3. Metrics, Backups, And Audit - -- [x] 3.1 Persist bounded metrics samples with retention and authorized paginated safe queries. -- [x] 3.2 Persist backup records and recovery transitions with size/checksum/retention bounds and audit events. -- [x] 3.3 Add cross-owner/endpoint/signature/session rejection and safe projection tests for logs, artifacts, metrics, backups, and audits. - -## 4. Scoped Remote Adapters - -- [x] 4.1 Add declaration-backed adapter registry and Platform authorization/dispatch with timeout, cancel, retry, fencing, and audit semantics. -- [x] 4.2 Implement Run typed adapter execution checkpoints without arbitrary shell, raw sockets, unapproved hosts/credentials, or direct plugin access. -- [x] 4.3 Add remote adapter protocol/client/runtime tests for timeout, cancellation, stale attempt, wrong owner/endpoint, and safe result projection. - -## 5. Frontend And Verification - -- [x] 5.1 Add platform_web logs/artifacts/metrics/backups/adapter status, pagination/sequence/checksum/audit projections using existing theme primitives and auth handling. -- [x] 5.2 Add regression tests for restart/recovery/retention/checksum/idempotency and control/job/log/artifact channel isolation. -- [x] 5.3 Run plugin manifest/SDK tests, Platform Go tests, platform_web tests/typecheck/build, Run tests, strict OpenSpec validation, structure, shell/compose checks, and both repository diff checks; record only passing evidence. diff --git a/openspec/changes/implement-durable-platform-storage/.openspec.yaml b/openspec/changes/implement-durable-platform-storage/.openspec.yaml deleted file mode 100644 index aee4ef1..0000000 --- a/openspec/changes/implement-durable-platform-storage/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-07 diff --git a/openspec/changes/implement-durable-platform-storage/design.md b/openspec/changes/implement-durable-platform-storage/design.md deleted file mode 100644 index 6ff8d9a..0000000 --- a/openspec/changes/implement-durable-platform-storage/design.md +++ /dev/null @@ -1,62 +0,0 @@ -## Overview - -The platform needs two storage shapes: - -- **Metadata store** for users, AI providers, plugins, server instances, run endpoints, jobs, artifacts, log stream metadata, and audit events. -- **Log body store** for high-volume append/query log entries. - -The first can be MySQL or another transactional database. The second should not be a row-per-line table for large installations. This change implements a local durable metadata store and a segmented log body store using only the Go standard library, with interfaces that can later gain MySQL/PostgreSQL/ClickHouse/Loki adapters. - -## Metadata Storage - -Current repository interfaces stay unchanged. A new file-backed store wraps the existing in-memory store and persists a snapshot after successful create/update operations. It is suitable for local and single-node deployments, tests, and development environments where no external database is available. - -Configuration: - -- `PLATFORM_STORAGE_BACKEND=memory|file` -- `PLATFORM_DATA_DIR=` -- `PLATFORM_METADATA_PATH=` - -Default startup uses file-backed storage under `.platform-data/metadata.json`, so data survives restarts. Tests can continue using `repo.NewMemoryStore()`. - -The file store is not positioned as a multi-writer clustered database. A future MySQL adapter should implement the same `repo.Store` interfaces and keep database table models in `platform/model`. - -## Log Body Storage - -`CoreService` currently stores log bodies in memory maps. This change introduces a `LogBodyStore` service boundary: - -- `AppendBatch(streamID, batch)` for validated, contiguous batch appends. -- `GetBatch(streamID, firstSeq)` for duplicate/conflict detection. -- `Query(streamID, afterSeq, limit)` for bounded cursor reads. - -The local durable implementation writes JSONL segment files: - -- Directory: `//` -- Segment naming: `segment-00000000000000000001.jsonl` using the first sequence in that segment. -- Each line is one `LogEntry`, keeping append and recovery simple. -- A small in-memory index is rebuilt on startup from segment files. - -This keeps control, jobs, and log upload channel semantics unchanged. It also avoids turning MySQL into a log body sink. For large production deployments, the same boundary can route log bodies to ClickHouse/Loki/OpenSearch/object storage and keep MySQL for metadata, stream state, retention policy, and query indexes. - -## Security And Boundaries - -- Storage paths are platform-owned configuration; they are never returned to plugins or frontend responses. -- Log query APIs still return bounded entries only. -- Run session tokens and bearer sessions remain service-side only. -- No raw database credentials are exposed through DTOs. - -## Failure Modes - -- File store creation fails fast on invalid or unwritable paths. -- Snapshot writes use temp-file then rename to avoid partial metadata files. -- Log segment writes return service errors instead of acknowledging batches that were not durably written. -- Existing in-memory tests remain valid; new tests cover restart/reload behavior for file metadata and log body stores. - -## Validation - -- Unit tests for file-backed metadata persistence across store reloads. -- Unit tests for segmented log store append, duplicate lookup, cursor query, and reload. -- API/service tests for default router admin persistence and log ingest behavior. -- `go test ./...` in `platform`. -- `scripts/check-structure.sh`. -- `openspec validate implement-durable-platform-storage --strict`. diff --git a/openspec/changes/implement-durable-platform-storage/proposal.md b/openspec/changes/implement-durable-platform-storage/proposal.md deleted file mode 100644 index 9a2d5d3..0000000 --- a/openspec/changes/implement-durable-platform-storage/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -## Why - -The platform currently uses in-memory repositories and in-memory log buffers, so users, sessions, jobs, server state, artifacts, and logs disappear when the platform process restarts. Hundreds or thousands of game servers also make row-per-log-line relational storage a poor default: platform metadata needs a durable database, while log bodies need append-friendly segmented storage with bounded cursor reads. - -## What Changes - -- Add configurable durable platform storage so the default local platform process no longer depends on volatile in-memory repositories. -- Add a local file-backed metadata store for immediate durable operation without external database credentials. -- Add a segmented log body store that persists log entries by stream and segment on disk, keeping log query semantics and duplicate detection intact. -- Update platform startup configuration to choose memory or durable local storage with explicit data/log paths. -- Document that MySQL/PostgreSQL-style databases are appropriate for platform metadata and log indexes, while high-volume log bodies should use segmented object/file storage or a purpose-built log backend such as ClickHouse/Loki/OpenSearch in later changes. - -## Capabilities - -### New Capabilities -- `durable-platform-storage`: Platform metadata and log bodies survive process restarts through configurable durable storage backends. - -### Modified Capabilities -- None. - -## Impact - -- Affected backend areas: `platform/config`, `platform/repo`, `platform/service`, `platform/api`, `platform/cmd/platform`, `platform/protocol`. -- No frontend page changes. -- No external paid services, real database credentials, or network downloads are required for this implementation. -- Future MySQL or analytics log backends can be added behind the new storage boundaries without exposing raw host paths, credentials, or direct sockets to plugins or the frontend. diff --git a/openspec/changes/implement-durable-platform-storage/specs/durable-platform-storage/spec.md b/openspec/changes/implement-durable-platform-storage/specs/durable-platform-storage/spec.md deleted file mode 100644 index 4ee61a2..0000000 --- a/openspec/changes/implement-durable-platform-storage/specs/durable-platform-storage/spec.md +++ /dev/null @@ -1,57 +0,0 @@ -## ADDED Requirements - -### Requirement: Durable Metadata Store - -The platform SHALL support a configurable durable metadata store so platform resources survive process restarts without relying on in-memory maps. - -#### Scenario: File-backed platform metadata survives restart - -- **GIVEN** the platform is configured with the file storage backend and a metadata file path -- **WHEN** users, server instances, jobs, artifacts, log streams, or other platform resources are created or updated -- **THEN** the metadata SHALL be persisted to disk -- **AND** recreating the store from the same file SHALL restore those resources. - -#### Scenario: Memory backend remains available for tests - -- **GIVEN** tests or development code explicitly request the memory backend -- **WHEN** the platform creates a store -- **THEN** it SHALL use the existing in-memory repository behavior without filesystem persistence. - -#### Scenario: Startup seeds local admin once - -- **GIVEN** durable metadata already contains the local platform administrator -- **WHEN** the platform starts again -- **THEN** startup seeding SHALL be idempotent and SHALL NOT overwrite the existing user. - -### Requirement: Segmented Log Body Storage - -The platform SHALL store high-volume log bodies in an append-friendly segmented log store instead of relying on in-memory maps or a row-per-log-line metadata database. - -#### Scenario: Log ingest persists entries durably - -- **GIVEN** a valid contiguous log batch for an existing stream -- **WHEN** the platform accepts the batch -- **THEN** the log body entries SHALL be written to the configured log body store before the stream latest sequence is advanced. - -#### Scenario: Duplicate batch detection survives reload - -- **GIVEN** a log batch has already been accepted and the platform restarts -- **WHEN** the same batch is submitted again -- **THEN** the platform SHALL return a duplicate acknowledgement when the first sequence, last sequence, and checksum match. - -#### Scenario: Cursor query stays bounded - -- **GIVEN** a log stream has many persisted entries -- **WHEN** a client queries after a sequence with a limit -- **THEN** the platform SHALL return at most the requested bounded number of entries ordered by sequence -- **AND** SHALL include next and latest sequence metadata. - -### Requirement: Storage Backend Boundaries - -The platform SHALL separate metadata storage from log body storage and SHALL NOT expose storage paths, database credentials, run sockets, or raw credentials through API responses. - -#### Scenario: Storage details remain platform-owned - -- **GIVEN** plugins or frontend clients request platform resources, logs, artifacts, or bridge actions -- **WHEN** responses are generated -- **THEN** they SHALL include only bounded DTO data and SHALL NOT include filesystem paths, database DSNs, direct storage backend URLs, run session tokens, or bearer tokens. diff --git a/openspec/changes/implement-durable-platform-storage/tasks.md b/openspec/changes/implement-durable-platform-storage/tasks.md deleted file mode 100644 index e92110b..0000000 --- a/openspec/changes/implement-durable-platform-storage/tasks.md +++ /dev/null @@ -1,25 +0,0 @@ -## 1. OpenSpec Artifacts - -- [x] 1.1 Create proposal, design, spec, and tasks for durable metadata and log storage. -- [x] 1.2 Validate the change with `openspec validate implement-durable-platform-storage --strict`. - -## 2. Durable Metadata Store - -- [x] 2.1 Add platform storage configuration for backend, metadata path, data directory, and log directory. -- [x] 2.2 Implement a file-backed metadata store that persists repository snapshots and reloads them. -- [x] 2.3 Wire platform startup to use the configured durable store by default while keeping memory store available. -- [x] 2.4 Add tests proving metadata persistence and default admin idempotency across store reloads. - -## 3. Segmented Log Body Store - -- [x] 3.1 Add a `LogBodyStore` service boundary with append, duplicate lookup, and cursor query operations. -- [x] 3.2 Implement an in-memory log body store for tests and a file-segment log body store for durable startup. -- [x] 3.3 Refactor log ingest/query service code to use `LogBodyStore` and persist before advancing stream metadata. -- [x] 3.4 Add tests for duplicate acknowledgement and cursor query after log store reload. - -## 4. Documentation And Verification - -- [x] 4.1 Document storage backend choices and log storage guidance in platform protocol/API docs. -- [x] 4.2 Run `cd platform && go test ./...`. -- [x] 4.3 Run `scripts/check-structure.sh`. -- [x] 4.4 Run `openspec validate implement-durable-platform-storage --strict`. diff --git a/openspec/changes/implement-game-client-bridge-and-scum-operations/.openspec.yaml b/openspec/changes/implement-game-client-bridge-and-scum-operations/.openspec.yaml deleted file mode 100644 index 29e56d8..0000000 --- a/openspec/changes/implement-game-client-bridge-and-scum-operations/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-20 diff --git a/openspec/changes/implement-game-client-bridge-and-scum-operations/design.md b/openspec/changes/implement-game-client-bridge-and-scum-operations/design.md deleted file mode 100644 index 36374aa..0000000 --- a/openspec/changes/implement-game-client-bridge-and-scum-operations/design.md +++ /dev/null @@ -1,98 +0,0 @@ -## Context - -The current platform already has the foundation for plugin runtime declarations, Client Manager build/deploy/control/update/rollback/uninstall, component-key registration, heartbeat, safe status projections, log ingest, artifact records, config diff review, remote access declarations, and backup metadata. The SCUM example manifest also declares a `custom-client` lifecycle and `game-client.bridge` capability, but the bridge is only a placeholder: there is no durable command domain, snapshot ingest/query model, bridge service, repository, DTO, or API surface. - -The reference SCUM repositories contain useful behavior: a companion client that can interact with the game window and OCR, server operations and command flows, SCUM log parsing, player/squad/vehicle/flag state, backups, restarts, rewards, events, and database-backed queries. They also contain patterns that must not be copied into this platform: shared credentials, insecure TLS skip, arbitrary terminal commands, arbitrary SQL, raw host paths, direct run sockets, direct cloud credentials, and SCUM business rules embedded into executor code. - -The intended ownership model is: - -- `game.scum` plugin owns SCUM semantics: pages, schemas, commands, players, squads, vehicles, flags, logs, database query templates, backup/restart/event policy, and `scum_client` companion behavior. -- Platform owns generic product infrastructure: auth, permission checks, durable queue, scheduling, audit, persistence, safe projections, and plugin-page APIs. -- Independent Run owns generic privileged execution only: process supervision, bounded file/SQLite/archive operations, and artifact transfer. No SCUM-specific source tree is added to this repository. - -## Goals / Non-Goals - -**Goals:** - -- Introduce a generic Game Client Bridge that supports authenticated companion clients, durable commands, command claims, acknowledgements, results, cancellation, expiry, idempotency, fencing, and typed snapshot ingestion/query. -- Add SCUM plugin operations that declare commands, snapshots, permissions, approval levels, page contracts, and companion-client expectations. -- Align the SCUM plugin manifest and the real `scum_client` model so generated packages use Platform Client Manager registration/session and bridge APIs instead of legacy `/api/v1/scum-clients/*` shared-token endpoints. -- Preserve channel isolation: game-client bridge traffic does not block run control, jobs, log ingest, or artifact transfer. -- Keep raw credentials, host paths, SQL text, direct sockets, and component secrets out of plugin pages and browser-visible DTOs. - -**Non-Goals:** - -- Do not implement SCUM business logic inside independent Run. -- Do not add a `run/` source tree back to this repository. -- Do not expose arbitrary shell, arbitrary SQL, generic remote desktop, direct cloud storage credentials, QQ/SMS/music/Douyin/provider workflows, host sales, billing, or unrelated SaaS marketplace features. -- Do not auto-ban or auto-punish players from heuristics in the first bridge phase; moderation begins as evidence, alerts, and operator-approved actions. -- Do not require all legacy SCUM robot features to ship in one implementation pass. - -## Decisions - -### Decision: Separate Game Client Bridge from Run lifecycle channels - -Platform will add a separate bridge domain and API for companion-client commands and snapshots. It will not reuse Run job leases, log batches, artifact chunks, or lifecycle control endpoints for game-window operations. - -Rationale: the existing run-contracts already state that the optional game client bridge is separate from run lifecycle, control registration, job handling, log ingest, and artifact transport. Keeping it separate prevents game commands or large snapshots from delaying heartbeat, job acknowledgement, log upload, or artifact transfer. - -Alternative considered: model every companion action as a Run job. That would blur operator-visible game operations with machine-side lifecycle execution, make SCUM commands look like generic privileged jobs, and increase the risk of leaking host/executor details into plugin pages. - -### Decision: Reuse Client Manager identity for companion clients - -`scum_client` will be deployed and supervised through the existing Client Manager lifecycle. Bridge APIs will require a valid component session bound to server instance, plugin, profile key, artifact, key generation, deployment generation, capabilities, and expiry. - -Rationale: the platform already has component-key generation, registration signatures, session revocation, heartbeat, health projection, and staged update/rollback. Extending from that identity avoids introducing a second secret model. - -Alternative considered: preserve legacy shared `SCUMClientCredential` and `/api/v1/scum-clients/hello|heartbeat|commands|results|snapshots`. That pattern cannot meet current security boundaries because one shared credential can outlive deployment generations, cannot fence stale packages cleanly, and was paired with insecure TLS behavior in the reference client. - -### Decision: Let plugins declare SCUM semantics, not Platform or Run - -The `game.scum` plugin will declare command catalog entries, snapshot schemas, query templates, page contracts, permission scopes, and approval levels. Platform will validate and persist these declarations, enforce auth/audit/lifecycle mechanics, and expose safe bridge APIs. Run will only execute generic capabilities requested by Platform. - -Rationale: the user correctly pointed out that these are SCUM plugin features. Putting SCUM semantics in Platform or Run would make the generic layers harder to reuse for other games and would violate the repository boundary that Run stays independent. - -Alternative considered: add first-class SCUM domain services directly to Platform. That would produce faster short-term UI but would freeze SCUM-specific concepts into platform core. - -### Decision: Use typed templates for database and file operations - -SCUM database reads will be exposed as plugin-declared read-only query templates with typed parameters and bounded result schemas. Backups and restart automation will be declared as SCUM logical policies but executed through generic Platform/Run operations. - -Rationale: the reference projects include useful SCUM.db and maintenance behavior, but arbitrary SQL or raw path access is not acceptable in the plugin-page boundary. Template declarations preserve common workflows while keeping operations reviewable and enforceable. - -Alternative considered: expose a generic SQL console or file browser to plugin pages. That would violate the existing platform rule that pages never receive DSNs, credentials, host paths, or direct Run endpoints. - -### Decision: Start moderation as evidence and approval flows - -Duplicate IP, mine, unlock, trade, and suspicious activity signals will initially create typed evidence, alerts, and suggested actions. Destructive or punitive actions require explicit operator approval and audit. - -Rationale: the reference robot contains powerful heuristics, but automatic punishments have high false-positive and abuse risk. Approval-first flows are safer and still make the signals useful. - -Alternative considered: directly port automatic punish/ban behavior. That is out of scope for the first bridge and would require separate governance requirements. - -## Risks / Trade-offs - -- Bridge queue and snapshot persistence can become broad quickly -> Mitigation: phase implementation around a small generic state machine, bounded payload sizes, schema versioning, retention, and server-scoped query filters. -- Real `scum_client` currently reads embedded `config.yaml` and does not parse the manifest's `--config config.json` argument -> Mitigation: update the SCUM plugin declaration and companion bootstrap together; support a generated config format that carries Platform registration settings without raw secrets in browser-visible DTOs. -- Snapshot schemas may drift from actual SCUM output -> Mitigation: version schemas, persist raw diagnostic excerpts only in redacted server-side records when needed, and keep plugin validators close to parser code. -- Remote read-only SQLite transport currently has gaps around payload/input propagation -> Mitigation: implement bridge database templates only after the remote access request path preserves declared inputs end to end. -- Frontend scope could sprawl into the whole legacy admin product -> Mitigation: first pages should cover operationally central SCUM surfaces only: client health, commands, players/sessions, vehicles/squads/flags, semantic logs, and maintenance. -- Long-running commands may conflict with deployment updates or session resets -> Mitigation: command claims include component session, deployment generation, lease expiry, and fencing tokens; stale sessions cannot ack or complete current commands. - -## Migration Plan - -1. Add Platform bridge domain, DTOs, repository interfaces, persistence, service methods, validators, routes, and tests. -2. Extend plugin manifest/schema validation for game-client command catalogs, snapshot schemas, approval levels, and page bridge contracts. -3. Update the SCUM example plugin manifest from local proof placeholders to a real `game.scum` lifecycle/client declaration aligned with generated config and Client Manager registration. -4. Add SCUM plugin page/API types and initial UI surfaces using safe Platform projections. -5. Adapt `scum_client` packaging expectations so it registers and heartbeats through Client Manager sessions and uses bridge command/snapshot APIs. -6. Add typed SCUM log parsing, database query templates, backup/restart declarations, and event/reward flows in later task groups after the bridge is verified. -7. Run `openspec validate implement-game-client-bridge-and-scum-operations --strict`, focused backend/frontend/plugin tests, and `scripts/check-structure.sh`. - -Rollback is feature-flag-like by declaration: servers without a deployed compatible Client Manager or without a plugin-declared bridge catalog see disabled bridge actions and no command dispatch. Existing run lifecycle, log ingest, and plugin registry behavior remains intact. - -## Open Questions - -- Which exact SCUM operations should be enabled in the first UI pass: read-only snapshots plus announcements, or also gifts/rewards/restarts? -- Should bridge command payload/result bodies be stored entirely in Platform DB for the first pass, or split larger artifacts to the artifact channel once payloads exceed a small threshold? -- Does `scum_client` remain in the external SCUM repository with generated packaging metadata, or should a minimal companion fixture be added under the plugin example for local smoke tests only? diff --git a/openspec/changes/implement-game-client-bridge-and-scum-operations/proposal.md b/openspec/changes/implement-game-client-bridge-and-scum-operations/proposal.md deleted file mode 100644 index 23db08a..0000000 --- a/openspec/changes/implement-game-client-bridge-and-scum-operations/proposal.md +++ /dev/null @@ -1,30 +0,0 @@ -## Why - -The SCUM companion client, robot workflows, and run-side helpers contain useful server-operations behavior, but their current shape mixes SCUM product semantics with ad hoc transport, credentials, host paths, and executor responsibilities. We need a first-party bridge that lets `game.scum` own SCUM-specific commands, snapshots, and pages while Platform and Run provide only generic secure infrastructure. - -## What Changes - -- Add a generic Game Client Bridge capability for durable, authenticated component commands, command lifecycle tracking, cancellation, expiry, idempotency, fencing, result recording, and versioned snapshot ingest/query. -- Add SCUM plugin operations on top of that bridge: command catalog, snapshot schemas, page contracts, permissions, and approval levels for players, squads, vehicles, flags, sessions, logs, database query templates, backups, restarts, and events. -- Adapt the real `scum_client` model into a plugin-owned companion component that registers through the Platform Client Manager path, heartbeats securely, claims bridge commands, returns results, and uploads typed snapshots. -- Keep independent Run free of SCUM business logic. Run may deploy, supervise, execute bounded process/file/SQLite/archive capabilities, and report lifecycle state, but SCUM semantics remain declared by the plugin and mediated by Platform. -- Replace unsafe legacy patterns from the reference projects with platform-mediated equivalents: no arbitrary terminal, no arbitrary SQL, no raw host paths, no shared credentials, no direct run sockets, no insecure TLS skip, and no script-based arbitrary URL self-update. - -## Capabilities - -### New Capabilities - -- `game-client-bridge`: Defines the platform bridge for authenticated companion clients, durable command queues, command result flow, cancellation/expiry semantics, snapshot ingestion, snapshot querying, retention, and audit trails. -- `scum-operations`: Defines the SCUM plugin-owned operational model, including typed commands, typed snapshots, pages, permissions, approval policy, log semantics, database query templates, backup/restart policies, and client integration requirements. - -### Modified Capabilities - -- None. - -## Impact - -- Backend Platform: new API routes, DTOs, domain types, repositories, services, persistence models, validation rules, permissions, and audit events for the Game Client Bridge. -- Platform Web: bridge-aware SCUM operation views that preserve the magical-girl crystal-moonlight console direction and do not receive raw credentials or host paths. -- Plugins: `plugins/examples/scum-server-plugin` gains real `game.scum` command catalogs, snapshot schemas, page contracts, permissions, and lifecycle/client declarations aligned with the real companion client. -- Companion Client: `scum_client` behavior is migrated from legacy shared-token endpoints to Platform Client Manager registration/session and bridge command/snapshot APIs. -- Run Executor: no SCUM-specific source is added to this repository; Run remains an independent generic executor for declared process, file, SQLite, artifact, and packaging operations. diff --git a/openspec/changes/implement-game-client-bridge-and-scum-operations/specs/game-client-bridge/spec.md b/openspec/changes/implement-game-client-bridge-and-scum-operations/specs/game-client-bridge/spec.md deleted file mode 100644 index 05fb6b0..0000000 --- a/openspec/changes/implement-game-client-bridge-and-scum-operations/specs/game-client-bridge/spec.md +++ /dev/null @@ -1,119 +0,0 @@ -## ADDED Requirements - -### Requirement: Component sessions authenticate bridge access - -The Platform SHALL require every Game Client Bridge request from a companion client to use a valid component session issued through the Client Manager registration flow. - -#### Scenario: Valid component session uses bridge capability - -- **WHEN** a deployed companion client registers with the current component key generation, deployment generation, and `game-client.bridge` capability -- **THEN** the Platform accepts bridge requests for the bound server instance, plugin, profile key, artifact, and session expiry window - -#### Scenario: Stale component session is rejected - -- **WHEN** a companion client uses a session from a revoked key generation, expired session, old deployment generation, or undeclared bridge capability -- **THEN** the Platform rejects the bridge request without returning raw key material or internal secret locations - -### Requirement: Commands are durable and fenced - -The Platform SHALL persist Game Client Bridge commands with lifecycle state, idempotency key, expiry, priority, declared command type, payload reference or inline bounded payload, target server instance, plugin, requester, approval state, claim lease, fencing token, and audit metadata. - -#### Scenario: Operator queues declared bridge command - -- **WHEN** an authorized operator queues a command declared by the active plugin bridge catalog -- **THEN** the Platform stores the command as pending, records an audit event, and exposes only safe command status to plugin pages - -#### Scenario: Duplicate idempotency key is reused - -- **WHEN** the same requester submits the same command type with the same idempotency key for the same server instance -- **THEN** the Platform returns the existing command instead of creating a duplicate command - -#### Scenario: Expired command is not claimed - -- **WHEN** a pending command has passed its expiry time before a companion client claims it -- **THEN** the Platform marks the command expired and prevents later claim or execution - -### Requirement: Companion clients claim and complete commands - -The Platform SHALL let authenticated companion clients claim pending bridge commands in bounded batches and complete them only with the active claim lease and fencing token. - -#### Scenario: Client claims pending command batch - -- **WHEN** an online companion client polls for bridge commands for its bound server instance and profile key -- **THEN** the Platform returns only eligible pending commands, marks them claimed, assigns leases, and includes fencing tokens - -#### Scenario: Stale claim cannot complete command - -- **WHEN** a companion client submits an ack or result with an expired lease, stale fencing token, or mismatched component session -- **THEN** the Platform rejects the update and leaves the current command state protected from stale completion - -#### Scenario: Command result is recorded - -- **WHEN** the active claimant completes a command with a success or failure result -- **THEN** the Platform stores sanitized result metadata, updates command status, records completion time, and emits an audit event - -### Requirement: Operators can cancel pending or claimed commands - -The Platform SHALL allow authorized operators to cancel bridge commands that are not already terminal and SHALL prevent cancelled commands from being executed or completed as successful. - -#### Scenario: Pending command is cancelled - -- **WHEN** an authorized operator cancels a pending command -- **THEN** the Platform marks the command cancelled and excludes it from future claim batches - -#### Scenario: Claimed command is cancelled before completion - -- **WHEN** an authorized operator cancels a claimed command -- **THEN** the Platform records the cancellation and rejects later success results from the old claim - -### Requirement: Snapshots are versioned and typed - -The Platform SHALL ingest Game Client Bridge snapshots only when their type, schema version, sequence, source component, payload shape, and retention policy match active plugin declarations. - -#### Scenario: Client uploads declared snapshot - -- **WHEN** a companion client uploads a snapshot that matches a declared snapshot type and schema version -- **THEN** the Platform stores it with server instance, plugin, profile key, source session, sequence, observed time, and retention metadata - -#### Scenario: Snapshot sequence is stale - -- **WHEN** a companion client uploads a snapshot with a sequence older than or equal to the latest accepted sequence for the same stream -- **THEN** the Platform rejects or quarantines the stale snapshot according to validation policy and does not replace the current projection - -#### Scenario: Plugin page queries snapshots - -- **WHEN** an authorized plugin page requests snapshots for an owned server instance -- **THEN** the Platform returns bounded safe projections without raw component secrets, host paths, direct sockets, or unbounded raw dumps - -### Requirement: Bridge traffic is isolated from Run channels - -The Platform SHALL keep Game Client Bridge command and snapshot traffic separate from Run control heartbeat, job acknowledgement, log ingest, and artifact transfer channels. - -#### Scenario: Large snapshot ingestion does not block control - -- **WHEN** a companion client uploads a large but allowed snapshot payload -- **THEN** Run control heartbeat, job acknowledgement, and log upload remain independently serviceable through their own channels - -#### Scenario: Bridge unavailable does not disable server lifecycle - -- **WHEN** the Game Client Bridge service is unavailable or no companion client is online -- **THEN** existing Run lifecycle actions, log ingest, artifact transfer, and Client Manager lifecycle projections continue to operate - -### Requirement: Browser-visible bridge DTOs are safe projections - -The Platform SHALL expose only safe bridge declarations, command statuses, results, snapshots, availability reasons, and audit references to plugin pages. - -#### Scenario: Plugin page loads bridge state - -- **WHEN** a plugin page loads bridge state for a server instance -- **THEN** the response excludes raw credentials, component keys, sessions, DSNs, host paths, direct Run endpoints, sockets, and storage provider credentials - -### Requirement: Bridge records are retained and audited - -The Platform SHALL apply bounded retention to bridge commands, results, snapshots, and audit references while preserving enough metadata for operator review and troubleshooting. - -#### Scenario: Retention job expires old bridge records - -- **WHEN** bridge records exceed configured retention limits -- **THEN** the Platform expires or compacts old records without exposing deleted payloads through plugin page APIs - diff --git a/openspec/changes/implement-game-client-bridge-and-scum-operations/specs/scum-operations/spec.md b/openspec/changes/implement-game-client-bridge-and-scum-operations/specs/scum-operations/spec.md deleted file mode 100644 index 8377dd3..0000000 --- a/openspec/changes/implement-game-client-bridge-and-scum-operations/specs/scum-operations/spec.md +++ /dev/null @@ -1,127 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM plugin owns SCUM operation semantics - -The `game.scum` plugin SHALL declare SCUM-specific commands, snapshot schemas, log event schemas, database query templates, permissions, approval levels, page contracts, and policy labels. - -#### Scenario: Platform loads SCUM bridge declarations - -- **WHEN** the Platform registers the SCUM plugin manifest -- **THEN** it validates the declared SCUM operation catalog and stores safe declarations without adding SCUM business logic to independent Run - -#### Scenario: Unknown SCUM command is rejected - -- **WHEN** a plugin page or operator attempts to queue a SCUM command that is not declared by the active SCUM plugin catalog -- **THEN** the Platform rejects the command before it reaches a companion client - -### Requirement: SCUM companion client uses Platform identity and bridge APIs - -The SCUM companion client SHALL register and heartbeat through the Platform Client Manager component-session flow and SHALL use Game Client Bridge command and snapshot APIs for game-window interaction. - -#### Scenario: Generated client package starts with platform config - -- **WHEN** a SCUM Client Manager package is generated and deployed -- **THEN** its runtime configuration aligns with the actual companion client bootstrap and includes only the material required to register with Platform through the current component-key generation - -#### Scenario: Legacy shared-token endpoint is not used - -- **WHEN** a SCUM companion client exchanges commands or snapshots with Platform -- **THEN** it does not use legacy shared-token `/api/v1/scum-clients/hello`, `/heartbeat`, `/commands`, `/results`, or `/snapshots` endpoints - -#### Scenario: Insecure TLS skip is not allowed - -- **WHEN** the SCUM companion client connects to Platform -- **THEN** it must not disable certificate verification through an unconditional insecure TLS setting - -### Requirement: SCUM snapshots cover core operational state - -The SCUM plugin SHALL define typed snapshot schemas for at least online sessions, players, squads, vehicles, flags or territories, and companion health diagnostics. - -#### Scenario: Player snapshot is ingested - -- **WHEN** the SCUM companion client uploads a declared player or online-session snapshot -- **THEN** Platform stores a versioned safe projection that plugin pages can query by server instance and observed time - -#### Scenario: Vehicle and flag snapshots are ingested - -- **WHEN** the SCUM companion client uploads declared vehicle, squad, flag, or territory snapshots -- **THEN** Platform validates the schema version and sequence before updating the current projection - -### Requirement: SCUM command catalog is bounded and permissioned - -The SCUM plugin SHALL expose only declared, typed, permission-scoped game commands such as announcements, player lookup, reward delivery, event actions, maintenance preparation, and safe companion diagnostics. - -#### Scenario: Operator queues announcement - -- **WHEN** an authorized operator queues a declared SCUM announcement command -- **THEN** Platform records the requester, approval state, command payload, idempotency key, and audit event before the companion client can claim it - -#### Scenario: Risky command requires approval - -- **WHEN** a SCUM command is marked as risky, destructive, economy-affecting, or punitive -- **THEN** Platform requires the declared approval level before making the command claimable - -### Requirement: SCUM moderation begins as evidence and review - -The SCUM plugin SHALL model suspicious duplicate IP, unlock, mine, trade, kill, admin, and economy signals as evidence, alerts, and operator-reviewed recommendations by default. - -#### Scenario: Suspicious event is parsed - -- **WHEN** SCUM semantic log parsing detects a suspicious event pattern -- **THEN** Platform records a typed evidence event or alert without automatically banning, punishing, or modifying the player - -#### Scenario: Operator approves punitive action - -- **WHEN** an operator chooses a punitive SCUM action from a recommendation -- **THEN** Platform applies the command catalog permission and approval rules before dispatching any companion command - -### Requirement: SCUM logs are parsed into typed events - -The SCUM plugin SHALL define typed semantic log events for chat, login, logout, kill, trade, mine, unlock, admin, and performance records. - -#### Scenario: Chat log event is parsed - -- **WHEN** SCUM chat log lines are ingested -- **THEN** Platform stores typed chat events that plugin pages can filter by player, time range, and server instance - -#### Scenario: Performance log event updates metrics - -- **WHEN** SCUM performance log lines are ingested -- **THEN** Platform maps FPS and entity counts into bounded metric projections and alert inputs - -### Requirement: SCUM database access uses read-only templates - -The SCUM plugin SHALL declare read-only SCUM.db query templates with typed parameters, bounded result schemas, and permission scopes instead of exposing arbitrary SQL. - -#### Scenario: Operator runs player lookup template - -- **WHEN** an authorized operator runs a declared player lookup query template -- **THEN** Platform dispatches a bounded read-only request and returns only the declared result columns - -#### Scenario: Arbitrary SQL is requested - -- **WHEN** a plugin page submits SQL text that is not backed by a declared query template -- **THEN** Platform rejects the request and does not dispatch it to Run or the companion client - -### Requirement: SCUM backup and restart policy remains plugin-declared - -The SCUM plugin SHALL declare backup scopes, retention policy, restart schedules, warning announcements, and update-check policy as SCUM operational policy while Platform and Run execute only generic jobs. - -#### Scenario: Scheduled restart is prepared - -- **WHEN** a SCUM restart schedule reaches its warning window -- **THEN** Platform queues declared SCUM announcement commands and generic lifecycle actions according to plugin policy and operator approvals - -#### Scenario: Backup is requested - -- **WHEN** an authorized operator requests a SCUM backup -- **THEN** Platform records the logical SCUM backup scope and dispatches generic archive/artifact operations without exposing raw host paths or storage credentials to the plugin page - -### Requirement: SCUM operation pages use safe Platform projections - -The SCUM plugin pages SHALL render bridge state, snapshots, commands, logs, backup/restart policy, and review flows using Platform-mediated DTOs only. - -#### Scenario: SCUM operations page loads - -- **WHEN** a user opens the SCUM operations page for a server instance -- **THEN** the page can display safe bridge status, companion health, current snapshots, command availability, and recent results without receiving raw secrets, host paths, DSNs, direct Run sockets, or component session material diff --git a/openspec/changes/implement-game-client-bridge-and-scum-operations/tasks.md b/openspec/changes/implement-game-client-bridge-and-scum-operations/tasks.md deleted file mode 100644 index 751fceb..0000000 --- a/openspec/changes/implement-game-client-bridge-and-scum-operations/tasks.md +++ /dev/null @@ -1,53 +0,0 @@ -## 1. Platform Bridge Foundation - -- [x] 1.1 Add Game Client Bridge domain types for commands, command states, claims, results, cancellations, snapshots, snapshot streams, retention metadata, and audit references. -- [x] 1.2 Add bridge DTOs and validation rules for queue, claim, ack/result, cancel, snapshot ingest, snapshot query, and safe browser projections. -- [x] 1.3 Add repository interfaces and in-memory or durable store implementations for bridge commands, snapshots, stream sequence tracking, and retention queries. -- [x] 1.4 Add service methods for command creation, idempotency reuse, claim leasing, fencing-token checks, terminal result handling, cancellation, expiry, and audit recording. -- [x] 1.5 Add component-session authorization checks so only current Client Manager sessions with `game-client.bridge` can claim commands or upload snapshots. - -## 2. Platform API Surface - -- [x] 2.1 Add operator-facing API routes for listing bridge declarations/status, queueing declared commands, cancelling commands, reading command results, and querying snapshots. -- [x] 2.2 Add companion-facing API routes for claiming command batches, acknowledging commands, posting results, uploading snapshots, and reporting bridge diagnostics. -- [x] 2.3 Ensure bridge routes expose only safe projections and never expose component keys, sessions, secret refs, host paths, DSNs, direct Run endpoints, sockets, or storage credentials. -- [x] 2.4 Add bridge retention and expiry reconciliation paths for old commands, old results, stale claims, and expired snapshots. - -## 3. Plugin Manifest and SDK Contracts - -- [x] 3.1 Extend plugin manifest schema and validation for game-client command catalogs, snapshot schemas, approval levels, permissions, retention policy, and page bridge contracts. -- [x] 3.2 Extend plugin SDK/API types so plugin pages can call Platform-mediated bridge actions without receiving raw executor or component secrets. -- [x] 3.3 Fix remote-access request input propagation needed by declared read-only database templates before enabling SCUM.db query operations. -- [x] 3.4 Add manifest/schema tests for rejected arbitrary SQL, arbitrary shell, raw paths, undeclared commands, unsafe capabilities, and missing approval metadata. - -## 4. SCUM Plugin Operations - -- [x] 4.1 Update the SCUM plugin manifest from local proof placeholders to real `game.scum` operation declarations for lifecycle, client manager, bridge commands, snapshots, logs, and pages. -- [x] 4.2 Align SCUM Client Manager packaging/config declarations with the real companion client bootstrap instead of nonexistent `configs/client.template.json`, `config.json`, and unsupported `--config` assumptions. -- [x] 4.3 Add typed SCUM snapshot schemas for online sessions, players, squads, vehicles, flags or territories, and companion health diagnostics. -- [x] 4.4 Add bounded SCUM command catalog entries for announcements, safe diagnostics, player lookup, reward/event flows, restart preparation, and maintenance actions with permissions and approval levels. -- [x] 4.5 Add read-only SCUM.db query template declarations with typed parameters and bounded result schemas. -- [x] 4.6 Add SCUM semantic log event declarations for chat, login, logout, kill, trade, mine, unlock, admin, and performance events. - -## 5. Frontend Operations Surface - -- [x] 5.1 Add Platform Web API client/types for bridge declarations, command status, command results, snapshot projections, approval state, and bridge diagnostics. -- [x] 5.2 Add SCUM operations page contracts and route wiring through the existing plugin page bridge model. -- [x] 5.3 Build the first SCUM operations UI for companion health, command queue/results, players/sessions, vehicles/squads/flags, semantic logs, and maintenance policy using safe Platform projections. -- [x] 5.4 Preserve the `platform_web` magical-girl crystal-moonlight operations-console style and avoid page-local fixed decorative effects outside `MagicalParticleLayer`. - -## 6. Companion Client Integration - -- [x] 6.1 Define the generated companion config shape used by Platform Client Manager registration and bridge APIs without exposing browser-visible raw secrets. -- [x] 6.2 Adapt the SCUM companion client integration path away from legacy shared-token `/api/v1/scum-clients/*` endpoints toward Platform component-session registration, heartbeat, command claim/result, and snapshot upload. -- [x] 6.3 Remove unconditional insecure TLS behavior from the companion integration path and add tests or checks for secure transport defaults. -- [x] 6.4 Add local smoke fixtures or documentation showing how a compatible `scum_client` package claims a command and uploads a typed snapshot. - -## 7. Tests and Verification - -- [x] 7.1 Add backend unit tests for bridge idempotency, claim leasing, fencing, cancellation, expiry, result recording, component-session authorization, safe projections, and snapshot sequence handling. -- [x] 7.2 Add plugin manifest validation tests for the SCUM operation declarations and unsafe legacy-pattern rejection. -- [x] 7.3 Add frontend tests for SCUM bridge API typing, disabled availability reasons, command approval states, and safe rendering without secrets. -- [x] 7.4 Run focused Go and frontend tests covering changed packages. -- [x] 7.5 Run `openspec validate implement-game-client-bridge-and-scum-operations --strict`. -- [x] 7.6 Run `scripts/check-structure.sh`. diff --git a/openspec/changes/implement-local-debug-workspace/.openspec.yaml b/openspec/changes/implement-local-debug-workspace/.openspec.yaml deleted file mode 100644 index 8cceb8d..0000000 --- a/openspec/changes/implement-local-debug-workspace/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-08 diff --git a/openspec/changes/implement-local-debug-workspace/design.md b/openspec/changes/implement-local-debug-workspace/design.md deleted file mode 100644 index 7ace601..0000000 --- a/openspec/changes/implement-local-debug-workspace/design.md +++ /dev/null @@ -1,66 +0,0 @@ -## Context - -The platform now has real local capabilities across `platform/`, `run/`, `platform_web/`, and `plugins/`: platform can serve API-backed metadata, run can register and execute lifecycle jobs, platform_web can operate against platform APIs, and the local proof plugin can drive server lifecycle through platform-mediated contracts. The remaining local-development gap is repeatability. Contributors currently need to reconstruct proof commands, temporary data roots, setup payloads, frontend proxy settings, browser walkthrough steps, and cleanup procedures from scattered task evidence. - -This change creates a first-party local debug workspace. It should make the real stack easy to boot, reset, inspect, and verify without introducing production deployment scope or bypassing platform mediation. - -## Goals / Non-Goals - -**Goals:** -- Provide one documented and/or scripted local workflow for platform, run worker, platform_web, and the dev game plugin path. -- Use explicit local ports, environment variables, data roots, spool roots, log roots, and reset commands. -- Include smoke checks for platform health, run registration/heartbeat, plugin registration/installation, server lifecycle, logs/artifact references, and API-backed frontend behavior. -- Include browser walkthrough requirements for 首页、服务器管理、插件市场、用户管理、AI 提供商管理 and one plugin lifecycle path. -- Keep all plugin/browser operations platform-mediated and prove no raw host paths, credentials, direct sockets, run tokens, raw AI keys, or plugin-owned transport details are visible. - -**Non-Goals:** -- Do not add cloud deployment, billing, host sales, agent-provider flows, or unrelated SaaS marketplace behavior. -- Do not add Docker-only requirements; local workflow may coexist with Docker but must be runnable with repository commands. -- Do not replace the existing magical-girl crystal-moonlight platform_web style. -- Do not make plugins connect directly to run or expose run sockets/tokens to platform_web. -- Do not implement the later automated browser acceptance suite here; this change may define a manual/semiautomated walkthrough that a later change can automate. - -## Decisions - -1. Use repository-owned local scripts/docs instead of only OpenSpec task notes. - - The workflow must survive after the implementation chat. A script plus a short markdown guide is preferable to evidence-only commands because contributors need a stable entry point. The script should print the exact platform, run, and frontend commands and write logs to predictable local files. - - Alternative considered: keep commands solely in `tasks.md`. That satisfies the change once but does not improve day-to-day local debugging. - -2. Keep local data under a disposable workspace root. - - Platform metadata, platform log bodies, run workspace files, and run spool files should live under an explicit root such as `.local-debug/` or `/private/tmp/browser-local-debug-workspace`. Reset must remove only that root and should never delete broad user directories. - - Alternative considered: reuse `.platform-data` and `.run-workspace` defaults. That is convenient but makes reset behavior less auditable and can mix unrelated local experiments. - -3. Use the existing API-backed platform and Vite proxy shape. - - The frontend should run with `PLATFORM_API_PROXY=http://127.0.0.1:` and `VITE_PLATFORM_API_BASE_URL=/api/v1`, proving browser calls go through platform-owned APIs. Browser/plugin pages must not receive direct run URLs or run credentials. - - Alternative considered: have platform_web point directly at a run worker or plugin dev server. That violates the architecture boundaries and is explicitly out of scope. - -4. Seed only safe local fixtures. - - The local workflow may create a dev plugin, one or more server instances, and lifecycle action templates under the scoped run workspace. Fixtures must use logical IDs and harmless commands. They must not require real game binaries, raw credentials, shell launchers, direct sockets, absolute host paths in API payloads, or raw AI keys. - - Alternative considered: require a real game server installation. That would make the debug workflow too heavy and environment-specific for this stream step. - -## Risks / Trade-offs - -- Port collisions -> Provide configurable env vars and print the resolved ports before starting services. -- Background process cleanup is brittle -> Prefer explicit log/PID files and a stop/reset command over hidden shell process management. -- Smoke setup can drift from APIs -> Implement smoke using the same public/local API routes and plugin manifest validation used elsewhere. -- Browser walkthrough remains manual -> Record exact pages, expected API-backed signals, and forbidden-fragment scan requirements so the later acceptance-suite change can automate it. -- Reset can become dangerous -> Scope cleanup to the local debug root and document what is removed before removing it. - -## Migration Plan - -1. Add local debug documentation and scripts or command wrappers under the appropriate repository location. -2. Add or update tests/smoke checks for generated commands, safe reset scope, and API-backed local fixture setup. -3. Run the documented local stack and browser/API walkthrough. -4. Record evidence in `tasks.md`, run structure checks and strict OpenSpec validation, and advance the stream pointer. - -## Open Questions - -- None currently. If implementation reveals an existing script location convention, follow it rather than inventing a parallel tool layout. diff --git a/openspec/changes/implement-local-debug-workspace/proposal.md b/openspec/changes/implement-local-debug-workspace/proposal.md deleted file mode 100644 index 832cc64..0000000 --- a/openspec/changes/implement-local-debug-workspace/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -## Why - -The architecture stream now has real platform, run, platform_web, and plugin lifecycle proof, but reproducing that stack still requires ad hoc commands, temporary paths, manual fixture setup, and scattered evidence. A first-party local debug workspace is needed so contributors can boot the same API-backed workflow repeatedly, inspect logs clearly, reset state safely, and prove the console is not falling back to demo-only data. - -## What Changes - -- Add a local debug workflow that starts platform, run worker, platform_web, and one local game management plugin path with documented ports, environment variables, data roots, and log locations. -- Add reset and cleanup steps for local platform metadata, log body storage, run workspace state, run spool state, and frontend dev-server state. -- Add smoke verification that proves platform health, run registration/heartbeat, plugin registration/installation, server instance lifecycle, log/artifact references, and browser/API-backed console navigation. -- Add safety checks proving browser/plugin page surfaces do not expose host paths, raw credentials, run session tokens, direct sockets, raw AI provider keys, or plugin-owned transport details. -- Add concrete commands and browser walkthrough requirements so future implementation chats can verify the local debug workflow without inventing a new proof path. -- No breaking changes are expected; this change standardizes local orchestration and verification around existing roots. - -## Capabilities - -### New Capabilities -- `local-debug-workspace`: Defines the local developer workflow for running platform, run, platform_web, and one game management plugin together with clear logs, reset steps, API-backed smoke checks, and browser walkthrough evidence. - -### Modified Capabilities -- None. - -## Impact - -- Affected roots: `platform/`, `run/`, `platform_web/`, `plugins/`, `scripts/`, and documentation. -- Expected implementation areas: local workflow scripts or docs, fixture/setup helpers, reset commands, smoke verification commands, browser walkthrough checklist, and OpenSpec task evidence. -- Validation impact: requires platform/run/plugin/frontend command checks where relevant, `scripts/check-structure.sh`, `openspec validate implement-local-debug-workspace --strict`, and a browser walkthrough for the API-backed console path. diff --git a/openspec/changes/implement-local-debug-workspace/specs/local-debug-workspace/spec.md b/openspec/changes/implement-local-debug-workspace/specs/local-debug-workspace/spec.md deleted file mode 100644 index bdccada..0000000 --- a/openspec/changes/implement-local-debug-workspace/specs/local-debug-workspace/spec.md +++ /dev/null @@ -1,67 +0,0 @@ -## ADDED Requirements - -### Requirement: Local debug workspace starts the real stack -The repository SHALL provide a local debug workflow that starts platform, run worker, platform_web, and one local game management plugin path using explicit local configuration. - -#### Scenario: Developer starts local debug services -- **WHEN** a developer follows the local debug workflow -- **THEN** the workflow MUST provide concrete commands or scripts for platform, run worker, and platform_web with explicit local ports, data directories, log directories, run workspace root, run spool root, frontend API proxy, and Vite API base URL - -#### Scenario: Local debug uses API-backed frontend -- **WHEN** platform_web is started by the local debug workflow -- **THEN** it MUST use platform-owned API routes through the configured proxy and MUST NOT require platform_web to connect directly to run or plugin-owned transports - -### Requirement: Local debug workspace can be reset safely -The repository SHALL provide reset or cleanup steps that remove only local debug workspace state and leave unrelated user files, repository source files, and non-debug service data untouched. - -#### Scenario: Developer resets local debug state -- **WHEN** a developer runs the documented reset path -- **THEN** platform metadata, platform log bodies, run workspace files, run spool files, local fixture state, and local service logs for the debug workspace MUST be removed or reinitialized only within the documented local debug root - -#### Scenario: Reset scope is auditable -- **WHEN** a contributor inspects the reset command or script -- **THEN** the command MUST show the exact local debug paths it removes and MUST NOT remove broad directories such as the repository root, home directory, `/Users`, `/private`, `/tmp`, or unrelated service data - -### Requirement: Local debug workflow seeds a safe game plugin lifecycle fixture -The local debug workflow SHALL seed or document a safe local game plugin fixture that can create and manage at least one server instance through platform-mediated lifecycle APIs. - -#### Scenario: Developer prepares lifecycle fixture -- **WHEN** the local debug setup creates plugin/server lifecycle data -- **THEN** it MUST register or reuse a local game plugin manifest, install the plugin through platform data, create at least one logical server instance, and use scoped run workspace lifecycle templates with harmless commands - -#### Scenario: Fixture preserves safety boundaries -- **WHEN** plugin lifecycle data is visible through platform APIs or platform_web -- **THEN** responses MUST NOT expose raw host paths, raw credentials, run session tokens, direct sockets, raw AI provider keys, shell launchers, or plugin-owned transport details - -### Requirement: Local debug workflow includes smoke verification -The local debug workflow SHALL include concrete smoke commands that prove the stack is healthy and API-backed before browser acceptance is claimed. - -#### Scenario: Smoke commands verify backend state -- **WHEN** smoke verification runs -- **THEN** it MUST check platform health, run endpoint registration or heartbeat, plugin registration/installation, server instance lifecycle state, queued or completed lifecycle jobs, and safe log/artifact references through platform APIs - -#### Scenario: Smoke commands reject demo-only fallback -- **WHEN** smoke verification inspects frontend or API state -- **THEN** it MUST prove platform_web is using the configured platform API and MUST flag local/demo fallback data as a failed smoke condition - -### Requirement: Browser walkthrough verifies first-party areas and safety -The local debug workflow SHALL include a browser walkthrough that verifies the API-backed console across required first-party areas and one plugin lifecycle path. - -#### Scenario: Browser walkthrough opens first-party areas -- **WHEN** the browser walkthrough runs -- **THEN** it MUST open 首页、服务器管理、插件市场、用户管理、AI 提供商管理 with an API-backed user session and confirm the pages are not local fallback views - -#### Scenario: Browser walkthrough verifies plugin lifecycle path -- **WHEN** the browser walkthrough operates a local debug server instance -- **THEN** it MUST use platform_web to inspect plugin/server details, trigger or verify a platform-mediated lifecycle action, observe operation history or job state, and confirm sibling/log/artifact references remain safe - -#### Scenario: Browser walkthrough scans visible sensitive fragments -- **WHEN** the browser walkthrough inspects visible page content -- **THEN** it MUST fail if `/Users/`, `/private/`, `unix://`, `tcp://`, `Bearer `, `sk-`, `password=`, `apiKeyRef`, `rawApiKey`, run session tokens, direct run URLs, or plugin-owned transport details are visible - -### Requirement: Local debug workflow is documented and verified -The change SHALL include documentation, tests or smoke checks, and final verification commands proving the local debug workflow is reproducible. - -#### Scenario: Verification commands run -- **WHEN** the change is complete -- **THEN** the documented test/build/smoke commands, `scripts/check-structure.sh`, and `openspec validate implement-local-debug-workspace --strict` MUST pass, and browser walkthrough evidence MUST be recorded if platform_web pages are touched or verified diff --git a/openspec/changes/implement-local-debug-workspace/tasks.md b/openspec/changes/implement-local-debug-workspace/tasks.md deleted file mode 100644 index 97326a5..0000000 --- a/openspec/changes/implement-local-debug-workspace/tasks.md +++ /dev/null @@ -1,83 +0,0 @@ -## 1. Local Debug Workflow Definition - -- [x] 1.1 Add or update repository documentation for the local debug workspace, including startup, ports, env vars, data roots, log files, reset, smoke verification, and browser walkthrough. -- [x] 1.2 Add scripts or command wrappers for starting platform, run worker, and platform_web with explicit local debug configuration. -- [x] 1.3 Add a reset/cleanup path that removes only the documented local debug root and prints or documents exactly what it deletes. -- [x] 1.4 Ensure the workflow does not require Docker-only infrastructure, external cloud services, real game binaries, raw credentials, raw AI keys, direct sockets, or browser/plugin direct access to run. - -## 2. Safe Plugin and Server Fixture - -- [x] 2.1 Add or document setup for one local game management plugin fixture using the existing dev plugin manifest or a safe local proof plugin. -- [x] 2.2 Add setup steps that create or reuse at least one server instance through platform-owned data/API paths and scoped run workspace lifecycle templates. -- [x] 2.3 Ensure fixture commands are harmless and bounded, and that API/platform_web responses expose only logical IDs, platform routes, job refs, log refs, artifact refs, and safe metadata. -- [x] 2.4 Add tests or smoke checks that reject raw host paths, raw credentials, run session tokens, direct sockets, raw AI provider keys, shell launchers, and plugin-owned transport details in fixture outputs. - -## 3. Smoke Verification Commands - -- [x] 3.1 Add concrete smoke commands for platform health, run endpoint registration/heartbeat, plugin registration/installation, server instance lifecycle state, lifecycle jobs, and log/artifact references. -- [x] 3.2 Add smoke verification that platform_web is configured with `PLATFORM_API_PROXY` and `VITE_PLATFORM_API_BASE_URL=/api/v1`, and that demo/local fallback data is treated as a failure. -- [x] 3.3 Run the documented backend smoke commands and record evidence. -- [x] 3.4 Run relevant unit/build checks for touched roots, such as `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1`, `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1`, `cd plugins && npm run typecheck && npm run test && npm run validate:manifest`, and `cd platform_web && npm run typecheck && npm test && npm run build` as applicable, then record evidence. - -## 4. Browser Walkthrough - -- [x] 4.1 Start the documented local debug stack and log the exact platform, run worker, and platform_web commands used. -- [x] 4.2 In a browser with an API-backed user session, open 首页、服务器管理、插件市场、用户管理、AI 提供商管理 and confirm no page is using local fallback data. -- [x] 4.3 Use the browser to inspect a local debug plugin/server lifecycle path, including server detail, plugin detail or marketplace data, operation history, and log/artifact references. -- [x] 4.4 Scan visible browser content for forbidden fragments: `/Users/`, `/private/`, `unix://`, `tcp://`, `Bearer `, `sk-`, `password=`, `apiKeyRef`, `rawApiKey`, run session tokens, direct run URLs, and plugin-owned transport details. -- [x] 4.5 Record browser walkthrough evidence in this tasks file only after the walkthrough has actually run. - -## 5. Final Verification and Stream Handoff - -- [x] 5.1 Record implementation evidence in this tasks file only after each command, smoke check, or browser walkthrough has actually run. -- [x] 5.2 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.3 Run `openspec validate implement-local-debug-workspace --strict` and record evidence. -- [x] 5.4 Update `openspec/changes/architecture-delivery-stream/delivery-plan.md` to mark `implement-local-debug-workspace` complete only after evidence exists and move the next queue item to active. -- [x] 5.5 Update `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` with the next implementation/generator handoff after this change closes. - -## Evidence - -- Added local debug documentation and repository pointers: - - `docs/local-debug-workspace.md` documents startup, ports, env vars, data roots, log files, reset scope, smoke verification, browser walkthrough, account credentials, and manual commands. - - `README.md` points contributors to `scripts/local-debug-start.sh`, `scripts/local-debug-smoke.sh`, `LOCAL_DEBUG_SELF_START=true`, and the local debug guide. - - `plugins/docs/local-development.md` documents the dev plugin fixture and safe API-backed proof path. -- Added local debug scripts: - - `scripts/local-debug-env.sh` centralizes `LOCAL_DEBUG_*`, platform, run, frontend, and forbidden-fragment settings. - - `scripts/local-debug-start.sh` starts platform, run worker, and platform_web with explicit logs, PIDs, ports, data roots, run workspace root, run spool root, Vite proxy, and fallback disabled. - - `scripts/local-debug-stop.sh` stops only tracked local-debug PIDs. - - `scripts/local-debug-reset.sh` refuses unexpected roots and deletes only `/.local-debug`, `/private/tmp/browser-local-debug-*`, or `/tmp/browser-local-debug-*`. - - `scripts/local-debug-smoke.sh` verifies platform health, API login, dev plugin manifest validation, plugin registration, run heartbeat, server lifecycle workflow creation, jobs, log streams, artifacts, marketplace refs, frontend proxy env, fallback disabled, and forbidden-fragment absence. -- Script syntax check passed: - - `bash -n scripts/local-debug-env.sh scripts/local-debug-start.sh scripts/local-debug-stop.sh scripts/local-debug-reset.sh scripts/local-debug-smoke.sh` -- Frontend verification passed: - - `cd platform_web && npm run typecheck` - - `cd platform_web && npm test` -> 11 test files, 49 tests passed. - - `cd platform_web && npm run build` -> Vite production build completed. -- Plugin verification passed: - - `cd plugins && npm run typecheck` - - `cd plugins && npm run test` -> 1 test file, 11 tests passed. - - `cd plugins && npm run validate:manifest` initially failed in the sandbox because `tsx` could not create an IPC pipe (`listen EPERM .../tsx-501/...pipe`); rerunning with elevated sandbox permissions passed and validated `examples/dev-game-plugin/manifest.json`. -- Backend verification passed: - - `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` passed for platform API, config, domain, DTO, model, repo, service, and validator packages. - - `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` initially failed in the sandbox because `httptest` could not bind local listeners; rerunning with elevated sandbox permissions passed for run API, config, protocol, runtime, and spool packages. -- Self-start local debug smoke passed: - - `LOCAL_DEBUG_PLATFORM_PORT=18187 LOCAL_DEBUG_WEB_PORT=5181 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-final LOCAL_DEBUG_SELF_START=true scripts/local-debug-smoke.sh` - - Evidence directory: `/private/tmp/browser-local-debug-final/smoke` - - Output confirmed platform health, dev plugin manifest validation, plugin API registration, run endpoint heartbeat, server lifecycle workflow creation, job/log/artifact/marketplace reference checks, and forbidden-fragment rejection. -- Browser walkthrough stack and seed smoke passed: - - Reset: `LOCAL_DEBUG_PLATFORM_PORT=18188 LOCAL_DEBUG_WEB_PORT=5182 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-walkthrough scripts/local-debug-reset.sh` - - Stack command: `/bin/zsh -lc 'LOCAL_DEBUG_PLATFORM_PORT=18188 LOCAL_DEBUG_WEB_PORT=5182 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-walkthrough scripts/local-debug-reset.sh; LOCAL_DEBUG_PLATFORM_PORT=18188 LOCAL_DEBUG_WEB_PORT=5182 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-walkthrough scripts/local-debug-start.sh; sleep 1200'` - - Seed smoke: `LOCAL_DEBUG_PLATFORM_PORT=18188 LOCAL_DEBUG_WEB_PORT=5182 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-walkthrough scripts/local-debug-smoke.sh` - - Evidence directory: `/private/tmp/browser-local-debug-walkthrough/smoke` -- Browser walkthrough evidence: - - Logged in at `http://127.0.0.1:5182` with `operator.local@example.test` / `operator-local`; login landed on `#/home` with API-backed platform data. - - Opened 首页 `#/home`: showed `平台概览`, `数据已加载`, `game.example:1 个实例`, and `运行节点 1`; no fallback/demo text and no forbidden fragments. - - Opened 服务器管理 `#/servers`: showed `Local Debug Example Server` / `server-local-debug`; no fallback/demo text and no forbidden fragments. - - Opened 插件市场 `#/plugins`: showed `game.example`, `artifact://manifests/game.example/0.1.0`, `process.install`, `process.start`, `process.stop`, `server.instances.read`, `jobs.dispatch`, `logs.query`, and `artifacts.open`; no fallback/demo text and no forbidden fragments. - - Opened 用户管理 `#/users`: showed `operator.local@example.test`, `账号 API 已连接`, and local admin metadata; no fallback/demo text and no forbidden fragments. - - Opened AI 提供商管理 `#/aiProviders`: showed API-backed provider rows with redacted secret refs (`secret://providers/openai`, `env://OLLAMA_API_KEY`); no fallback/demo text and no forbidden fragments. - - Opened server detail `#/servers/server-local-debug`: showed `Local Debug Example Server`, `game.example@0.1.0`, `run-local-debug`, lifecycle buttons, `日志`, `配置`, `插件控制`, `AI 助手`, and `操作历史`; no fallback/demo text and no forbidden fragments. - - The browser DOM snapshot helper failed with `incrementalAriaSnapshot is not a function`, so the walkthrough used read-only page evaluation to inspect visible text, buttons, URLs, and forbidden fragments. -- Final verification commands: - - `scripts/check-structure.sh` passed after implementation evidence was recorded. - - `openspec validate implement-local-debug-workspace --strict` passed after implementation evidence and stream handoff were updated; PostHog telemetry DNS errors, if emitted after success, do not affect the validation result. diff --git a/openspec/changes/implement-log-ingest-pipeline/.openspec.yaml b/openspec/changes/implement-log-ingest-pipeline/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-log-ingest-pipeline/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-log-ingest-pipeline/design.md b/openspec/changes/implement-log-ingest-pipeline/design.md deleted file mode 100644 index 76aa406..0000000 --- a/openspec/changes/implement-log-ingest-pipeline/design.md +++ /dev/null @@ -1,81 +0,0 @@ -## Context - -Run control and job lifecycle routes are implemented, but logs still exist only as metadata records. The architecture requires logs to be treated as durable historical data: run writes batches to a local spool before upload, platform acknowledges accepted sequence ranges, and artifacts must not block control, job, or log traffic. - -This change implements the first HTTP JSON log ingest path and an in-repository run spool abstraction. It keeps platform storage in memory and updates existing `LogStream` metadata because durable database/log backend selection is a later architecture decision. - -## Goals / Non-Goals - -**Goals:** - -- Define typed log ingest protocol payloads in `run/protocol` and matching platform DTO/domain contracts. -- Add platform log ingest API routes for batch upload and bounded stream cursor query. -- Validate run session continuity, stream identity, sequence ranges, checksums, and batch size. -- Track accepted log entries and latest acknowledged sequence in platform service state and existing `LogStream.LatestSeq`. -- Add a run-side local spool abstraction that persists unacknowledged batches to disk and removes acknowledged ranges only after platform ack. -- Extend `run/api.PlatformClient` with a typed log batch ingest method. -- Add tests for platform ack/query behavior, duplicate/out-of-order rejection, run spool retry retention, and client request/response behavior. - -**Non-Goals:** - -- No external log storage backends such as Loki, ClickHouse, OpenSearch, or Elasticsearch. -- No browser live tail, log websocket, AI log analysis windows, or frontend behavior. -- No compression codec implementation beyond typed metadata and checksum validation for the JSON payload. -- No artifact transfer, game client bridge, billing, cloud host sales, or direct plugin-to-run access. -- No raw host paths, raw credentials, direct sockets, or artifact chunks inside log payloads. - -## Decisions - -### Decision 1: HTTP JSON batch ingest first - -The initial ingest route uses `POST /api/v1/run/logs/batches` with typed JSON batches. This keeps the path testable, bounded, and independent from control, jobs, and artifacts. - -Alternative considered: streaming logs over the control or job channel. Rejected because logs are high-volume historical data and must not block heartbeat, claim/ack/result, or artifact traffic. - -### Decision 2: Platform validates contiguous sequence ranges - -Each batch carries `streamKey`, `firstSeq`, `lastSeq`, checksum, and entries. The platform accepts the next contiguous range, treats already-acknowledged duplicate batches as idempotent acks, and rejects sequence gaps or conflicting duplicates. - -Alternative considered: accepting any sequence order and sorting later. Rejected because retry/ack semantics need deterministic spool cleanup and missing ranges must be visible immediately. - -### Decision 3: Log body storage is in-memory for now - -The service stores accepted log entries in memory keyed by stream ID and updates existing `LogStream.LatestSeq`. This matches the current repository scope and lets later storage adapters replace the implementation behind service methods. - -Alternative considered: adding a local compressed segment storage backend now. Rejected because this change needs API semantics and run spool behavior first; backend choice remains open. - -### Decision 4: Run spool stores batches as JSON segment files - -The run-side spool writes one JSON file per unacknowledged batch in a caller-provided directory. Tests can inspect retry behavior without a daemon loop, and future uploaders can reuse the same abstraction. - -Alternative considered: purely in-memory spool. Rejected because the architecture requires local retention across temporary platform unavailability and restart. - -### Decision 5: Client stays transport-only - -`run/api.PlatformClient` will encode and decode log ingest requests and responses. Collector loops, file tailing, backpressure scheduling, and artifact priority throttling remain future runtime work. - -Alternative considered: implementing a background log uploader now. Rejected because that would expand scope beyond protocol, spool, and ack semantics. - -## Risks / Trade-offs - -- [Risk] In-memory platform log storage disappears on restart. Mitigation: keep storage behind `service.Core` and document this as early development behavior. -- [Risk] JSON spool files are not optimized for very large log volumes. Mitigation: enforce bounded batch sizes now; later changes can swap segment encoding without changing ack semantics. -- [Risk] Checksums only cover entries in this change. Mitigation: keep checksum metadata explicit and add compressed segment checksums when compression/chunking is implemented. -- [Risk] No background uploader means no automatic retry loop. Mitigation: tests cover retained batches and client upload behavior; scheduling remains a later runtime concern. - -## Migration Plan - -1. Add log protocol, DTO, domain, validation, and service contracts. -2. Add platform API handlers and tests for ingest and query. -3. Add run local spool implementation and tests. -4. Add run client method and tests. -5. Update protocol/route docs. -6. Verify with platform tests, run tests, structure check, and strict OpenSpec validation. - -Rollback before dependent changes is removal of the log ingest route/client/spool additions and this OpenSpec change. After artifact/server workflow changes depend on logs, rollback must use a new OpenSpec change. - -## Open Questions - -- Which production log body backend should be implemented first: local compressed segments, Loki, ClickHouse, OpenSearch, or Elasticsearch? -- What maximum batch size and compression settings should production use? -- How should browser live tail subscribe to stored logs without weakening durable ingest guarantees? diff --git a/openspec/changes/implement-log-ingest-pipeline/proposal.md b/openspec/changes/implement-log-ingest-pipeline/proposal.md deleted file mode 100644 index 31a0fd8..0000000 --- a/openspec/changes/implement-log-ingest-pipeline/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -The run job channel can now execute lifecycle work, but server and process logs still have no durable ingest path. This change adds the first log pipeline so run can spool logs locally, upload bounded batches, and receive sequence acknowledgements without mixing log traffic into control, job, or artifact channels. - -## What Changes - -- Add typed run log ingest protocol payloads for log entries, batch ingest requests, batch acknowledgements, and stream cursors. -- Add platform API routes that accept durable log batches, validate stream identity and sequence continuity, acknowledge accepted ranges, and expose bounded query by stream cursor. -- Extend platform service behavior to append log batches to existing log stream metadata, track latest acknowledged sequence, and reject duplicate or out-of-order batches. -- Add a run-side local spool/WAL abstraction that stores unacknowledged batches and removes only acknowledged sequence ranges. -- Extend the run-side platform client with typed log batch ingest calls. -- Add platform service/API tests and run spool/client tests covering retry, acknowledgement, duplicate/out-of-order rejection, and query behavior. - -## Capabilities - -### New Capabilities - -- `log-ingest-pipeline`: Durable run-to-platform log batch ingest, acknowledgement, local spool retention, and stream cursor query workflow. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` and `run/` only. -- Adds Go protocol/DTO/domain/service/API/spool code and tests for log ingest. -- Updates run/platform protocol and route documentation. -- Does not implement artifact transfer, browser live tail, external log backends, AI log analysis, frontend pages, billing, cloud host sales, or direct plugin/run access. diff --git a/openspec/changes/implement-log-ingest-pipeline/specs/log-ingest-pipeline/spec.md b/openspec/changes/implement-log-ingest-pipeline/specs/log-ingest-pipeline/spec.md deleted file mode 100644 index 66ec5ff..0000000 --- a/openspec/changes/implement-log-ingest-pipeline/specs/log-ingest-pipeline/spec.md +++ /dev/null @@ -1,74 +0,0 @@ -## ADDED Requirements - -### Requirement: Log ingest payloads are typed and bounded -The system SHALL define typed log ingest payloads for log entries, batch ingest requests, batch acknowledgements, and stream cursor queries without carrying artifact chunks, host paths, raw credentials, direct sockets, or unbounded inline data. - -#### Scenario: Log payloads are used -- **WHEN** run or platform code sends log ingest data -- **THEN** it MUST use named protocol/DTO types from dedicated protocol or DTO packages - -#### Scenario: Log batch stays bounded -- **WHEN** run uploads a log batch -- **THEN** the request MUST include run endpoint ID, session token, stream identity, sequence range, checksum, compression metadata, and bounded entries only - -### Requirement: Platform accepts durable log batches -The platform SHALL expose a log batch ingest endpoint that validates run session continuity, stream metadata, checksum, and sequence continuity before acknowledging accepted ranges. - -#### Scenario: Contiguous batch succeeds -- **WHEN** run uploads a valid batch whose first sequence follows the platform's latest acknowledged sequence for that stream -- **THEN** platform MUST store the entries, update latest acknowledged sequence, and return an accepted acknowledgement for the range - -#### Scenario: Duplicate acknowledged batch is retried -- **WHEN** run uploads a batch whose range is already fully acknowledged and checksum matches the stored range -- **THEN** platform MUST return an accepted idempotent acknowledgement without duplicating entries - -#### Scenario: Out-of-order batch is submitted -- **WHEN** run uploads a batch with a sequence gap or conflicting duplicate data -- **THEN** platform MUST return a JSON validation error and MUST NOT advance the acknowledged sequence - -### Requirement: Platform exposes bounded log stream query -The platform SHALL expose a bounded log query endpoint that returns entries for one stream after a cursor sequence and includes the next cursor. - -#### Scenario: Query returns entries after cursor -- **WHEN** a caller queries a stream after an acknowledged sequence -- **THEN** platform MUST return ordered entries after that cursor up to the requested limit and include the next cursor - -#### Scenario: Query target is missing -- **WHEN** a caller queries a missing stream -- **THEN** platform MUST return a JSON not found error - -### Requirement: Run spool retains unacknowledged batches -The run-side log spool SHALL persist unacknowledged batches locally and remove them only after platform acknowledgement covers their sequence range. - -#### Scenario: Platform upload fails -- **WHEN** a batch remains unacknowledged after an upload failure -- **THEN** the spool MUST retain the batch for retry - -#### Scenario: Platform acknowledges batch -- **WHEN** platform returns an acknowledgement covering a batch range -- **THEN** the spool MUST mark that range acknowledged and remove the batch from pending retry listing - -### Requirement: Run client uploads log batches -The run-side platform client SHALL provide a typed log batch ingest method that calls the platform log endpoint and decodes typed acknowledgement responses. - -#### Scenario: Run uploads log batch through client -- **WHEN** run code calls the log ingest client method -- **THEN** the client MUST send a JSON `POST` to `/api/v1/run/logs/batches` and decode the acknowledgement response - -#### Scenario: Platform rejects log batch -- **WHEN** the platform log ingest endpoint returns a non-success status -- **THEN** the run client MUST return an error and MUST NOT treat the batch as acknowledged - -### Requirement: Log ingest is documented separately from other channels -The run/platform route and protocol documentation SHALL identify implemented log ingest routes and explicitly keep control, job, artifact, and game client bridge transport separate. - -#### Scenario: Contributor inspects log docs -- **WHEN** a contributor opens run or platform protocol docs -- **THEN** the docs MUST show log batch ingest and query as implemented while artifact and game client channels remain separate - -### Requirement: Log ingest pipeline is verified -The change SHALL include platform service/API tests, run spool tests, run client tests, and retry/ack/query coverage. - -#### Scenario: Verification commands run -- **WHEN** the change is complete -- **THEN** `go test ./...` from `platform/`, `go test ./...` from `run/`, `scripts/check-structure.sh`, and `openspec validate implement-log-ingest-pipeline --strict` MUST pass diff --git a/openspec/changes/implement-log-ingest-pipeline/tasks.md b/openspec/changes/implement-log-ingest-pipeline/tasks.md deleted file mode 100644 index 9594591..0000000 --- a/openspec/changes/implement-log-ingest-pipeline/tasks.md +++ /dev/null @@ -1,35 +0,0 @@ -## 1. Log Contracts - -- [x] 1.1 Add typed run log ingest protocol payloads in `run/protocol` for entries, batch ingest, acknowledgements, and stream cursors. -- [x] 1.2 Add matching platform DTO/domain contracts and conversion helpers for log batch ingest and query. -- [x] 1.3 Add validation rules for bounded log batches, stream identity, sequence ranges, checksum, and query limits. - -## 2. Platform Log Ingest - -- [x] 2.1 Extend platform service behavior to ingest contiguous batches, acknowledge duplicates, reject out-of-order/conflicting batches, update `LogStream.LatestSeq`, and query entries after a cursor. -- [x] 2.2 Implement platform log ingest/query HTTP routes using named DTOs and service methods. -- [x] 2.3 Add platform service/API tests for accepted batches, duplicate ack, out-of-order rejection, missing stream, and cursor query. - -## 3. Run Log Spool And Client - -- [x] 3.1 Implement a run-side local spool abstraction that writes pending batches to disk, lists them for retry, and removes acknowledged ranges. -- [x] 3.2 Extend `run/api.PlatformClient` with a typed log batch ingest method. -- [x] 3.3 Add run spool/client tests for retry retention, acknowledgement cleanup, request path, JSON payload, response decoding, and platform error handling. - -## 4. Documentation - -- [x] 4.1 Update run and platform protocol/route documentation to mark log batch ingest/query implemented and keep control/job/artifact/game-client channels separate. - -## 5. Verification - -- [x] 5.1 Run `go test ./...` from `platform/` and record evidence. -- [x] 5.2 Run `go test ./...` from `run/` and record evidence. -- [x] 5.3 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.4 Run `openspec validate implement-log-ingest-pipeline --strict` and record evidence. - -## Evidence - -- 2026-07-03: `go test ./...` from `platform/` passed. -- 2026-07-03: `go test ./...` from `run/` passed. -- 2026-07-03: `scripts/check-structure.sh` passed with `structure check passed`. -- 2026-07-03: `openspec validate implement-log-ingest-pipeline --strict` passed with `Change 'implement-log-ingest-pipeline' is valid`. diff --git a/openspec/changes/implement-platform-api-surface/.openspec.yaml b/openspec/changes/implement-platform-api-surface/.openspec.yaml deleted file mode 100644 index 8e26fbe..0000000 --- a/openspec/changes/implement-platform-api-surface/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-02 diff --git a/openspec/changes/implement-platform-api-surface/design.md b/openspec/changes/implement-platform-api-surface/design.md deleted file mode 100644 index f29ec59..0000000 --- a/openspec/changes/implement-platform-api-surface/design.md +++ /dev/null @@ -1,79 +0,0 @@ -## Context - -`platform/` already contains typed domain resources, DTO contracts, model projections, validators, repositories, and `service.Core` workflows for the first platform resources. The current executable only exposes `/healthz`, so frontend, run, and plugin changes cannot yet rely on HTTP behavior for users, game plugins, server instances, AI providers, run endpoints, jobs, artifacts, log streams, or audit events. - -This change stays inside `platform/` and implements the first HTTP adapter layer over the existing core service. It must preserve the repository structure rules: handlers belong in `api/`, request/response DTOs in `dto/`, domain rules in `domain/` and `validator/`, and storage concerns in `repo/` or future persistence packages. - -## Goals / Non-Goals - -**Goals:** - -- Expose create, list, and detail HTTP routes for core platform resources. -- Keep handlers as adapters that decode named DTOs, call `service.Core`, and encode named DTO responses. -- Return deterministic JSON error responses for malformed JSON, validation failures, duplicate IDs, missing resources, and unexpected failures. -- Preserve AI provider redaction by returning `apiKeyRef` only and never raw API key material. -- Make router construction injectable for tests and future persistence while retaining an in-memory default for local development. -- Update `platform/api/routes.md` to reflect implemented routes. - -**Non-Goals:** - -- No authentication, session, RBAC, or authorization engine. -- No SQL database, migrations, or external persistence dependency. -- No frontend or run-side implementation. -- No run job claim/ack/result protocol implementation beyond platform-side job resource creation and query. -- No plugin page bridge implementation, AI invocation endpoint, file content transfer, log body ingest, or artifact chunk transfer. -- No billing, cloud host sales, agent-provider/cloud-provider workflows, or unrelated SaaS marketplace features. - -## Decisions - -### Decision 1: Use `net/http` ServeMux with explicit method dispatch - -The platform will keep using the Go standard library. Route wiring will use `http.ServeMux` path patterns, `PathValue` for detail routes, and explicit method dispatch inside resource handlers so unsupported methods can return the named JSON error DTO. - -Alternative considered: adding a third-party router. Rejected because the API surface is still small and the current module has no external runtime dependencies. - -### Decision 2: Router accepts `service.Core` - -`api.NewRouter()` will build the current in-memory service for local execution, while `api.NewRouterWithCore(core service.Core)` will allow tests and later persistence changes to provide a service implementation. - -Alternative considered: constructing repositories directly inside every handler. Rejected because it hides storage choices in transport code and bypasses the service layer that already owns cross-resource invariants. - -### Decision 3: DTO package owns request conversion and API envelopes - -Create-request DTOs will expose `ToDomain()` helpers. Response DTOs and list/error envelopes will remain named structs under `platform/dto` so handlers do not define request/response shapes inline. - -Alternative considered: constructing ad hoc response maps in handlers. Rejected because API contracts must remain discoverable and testable. - -### Decision 4: Handlers map service errors to stable HTTP errors - -Handlers will translate `validator.ValidationError` to `400`, malformed JSON to `400`, `repo.ErrNotFound` to `404`, `repo.ErrDuplicate` to `409`, and unexpected errors to `500`. All errors will use `dto.ErrorResponse`. - -Alternative considered: returning plain-text `http.Error`. Rejected because clients need predictable JSON responses and AGENTS.md requires named error DTOs. - -### Decision 5: Implement metadata routes only for logs and artifacts - -This change implements log stream metadata and artifact metadata resources. Chunk upload/download, durable log ingest, tail transport, and storage adapters remain future changes because they affect run communication channels and transfer backpressure. - -Alternative considered: implementing chunk and ingest endpoints now. Rejected because the delivery stream has separate changes for run channels, logs, and artifacts. - -## Risks / Trade-offs - -- [Risk] In-memory default storage loses data on restart. Mitigation: document it as local development wiring and keep router injection ready for future persistence. -- [Risk] Create/list/get routes are narrower than the full route catalog. Mitigation: document deferred lifecycle, chunk, ingest, and plugin bridge behavior explicitly in `platform/api/routes.md`. -- [Risk] Query filter values are string-based and rely on domain enum strings. Mitigation: keep filters narrow and let create/update validation remain in the service and validator layers. - -## Migration Plan - -1. Add DTO conversion helpers, list envelopes, and error DTOs. -2. Add API router wiring and handlers over `service.Core`. -3. Update route catalog documentation. -4. Add focused handler tests for route behavior, validation/error mapping, and AI provider redaction. -5. Verify with `go test ./...` from `platform/`, `scripts/check-structure.sh`, and strict OpenSpec validation. - -Rollback before dependent changes is removal of the API handler additions and this OpenSpec change. After frontend, run, or plugin changes consume these routes, rollback must be handled through a new OpenSpec change. - -## Open Questions - -- Which authentication/session mechanism will wrap these routes first? -- Which persistent repository implementation should replace the in-memory default? -- Which API pagination and sorting contract should be introduced once lists can grow beyond development-scale data? diff --git a/openspec/changes/implement-platform-api-surface/proposal.md b/openspec/changes/implement-platform-api-surface/proposal.md deleted file mode 100644 index 9b88913..0000000 --- a/openspec/changes/implement-platform-api-surface/proposal.md +++ /dev/null @@ -1,30 +0,0 @@ -## Why - -The platform backend has typed core resources and service workflows, but clients still cannot exercise them through HTTP. This change adds the first platform API surface so later frontend, run, and plugin work can depend on stable handler behavior instead of calling services directly. - -## What Changes - -- Add HTTP route handlers for core platform resources using the existing domain, DTO, validator, repository, and service packages. -- Support create, list, and detail workflows for users, AI providers, game management plugins, server instances, run endpoints, jobs, artifacts, log streams, and audit events. -- Return JSON error responses for malformed requests, validation failures, duplicates, and missing resources. -- Preserve AI provider redaction by returning only API key references and never raw provider keys. -- Wire the platform router to an in-memory service instance for local development while keeping handlers injectable for tests and future persistence. -- Update route catalog documentation to reflect the implemented API paths. - -## Capabilities - -### New Capabilities - -- `platform-api-surface`: HTTP API handlers, route wiring, request decoding, response encoding, and error behavior for core platform resources. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` only. -- Adds platform API handler code and focused handler tests. -- Extends DTO helpers for request-to-domain conversion and JSON error response contracts. -- Uses only Go standard library HTTP routing and the existing in-memory core service. -- Does not add authentication, authorization, SQL persistence, frontend behavior, run executor behavior, billing, cloud host sales, or direct plugin/run access. diff --git a/openspec/changes/implement-platform-api-surface/specs/platform-api-surface/spec.md b/openspec/changes/implement-platform-api-surface/specs/platform-api-surface/spec.md deleted file mode 100644 index 5713020..0000000 --- a/openspec/changes/implement-platform-api-surface/specs/platform-api-surface/spec.md +++ /dev/null @@ -1,82 +0,0 @@ -## ADDED Requirements - -### Requirement: Core resource HTTP routes are implemented -The platform SHALL expose HTTP JSON routes for create, list, and detail workflows for users, AI providers, game management plugins, server instances, run endpoints, jobs, artifacts, log streams, and audit events. - -#### Scenario: Resource is created through API -- **WHEN** a valid create request is posted to a core resource collection route -- **THEN** the platform MUST persist the resource through `service.Core` and return `201` with the corresponding named response DTO - -#### Scenario: Resource list is requested -- **WHEN** a client sends `GET` to a core resource collection route -- **THEN** the platform MUST return `200` with a named list response DTO containing resources from `service.Core` - -#### Scenario: Resource detail is requested -- **WHEN** a client sends `GET` to a core resource detail route with an existing resource ID -- **THEN** the platform MUST return `200` with the corresponding named response DTO - -### Requirement: API handlers use centralized DTO and service contracts -The platform SHALL keep API request, response, list, and error contracts in `platform/dto` and SHALL call `service.Core` for resource workflows. - -#### Scenario: Handler decodes request body -- **WHEN** an API handler accepts a request body -- **THEN** it MUST decode into a named DTO type from `platform/dto` and convert that DTO to a named domain type before calling `service.Core` - -#### Scenario: Handler returns response body -- **WHEN** an API handler returns a success or error response -- **THEN** it MUST encode a named DTO response type and MUST NOT define response structs inside handler functions - -### Requirement: API errors are stable JSON responses -The platform SHALL return named JSON error DTOs for malformed requests, validation failures, duplicate resources, missing resources, unsupported methods, and unexpected failures. - -#### Scenario: Invalid JSON is submitted -- **WHEN** a client posts malformed JSON to a core resource route -- **THEN** the platform MUST return `400` with a JSON error response - -#### Scenario: Validation fails -- **WHEN** a create request violates validator or service invariants -- **THEN** the platform MUST return `400` with a JSON error response and MUST NOT persist the resource - -#### Scenario: Duplicate resource is submitted -- **WHEN** a create request uses an ID that already exists -- **THEN** the platform MUST return `409` with a JSON error response - -#### Scenario: Missing resource is requested -- **WHEN** a client requests a resource ID that does not exist -- **THEN** the platform MUST return `404` with a JSON error response - -### Requirement: AI provider API preserves credential redaction -The AI provider API SHALL return redacted provider response DTOs that include secret references only and never raw API keys. - -#### Scenario: AI provider is created through API -- **WHEN** a valid AI provider create request is posted -- **THEN** the platform MUST return an `AIProviderResponse` containing `apiKeyRef` and MUST NOT include raw API key fields - -#### Scenario: Raw AI key is submitted as key reference -- **WHEN** an AI provider create request includes raw key material in `apiKeyRef` -- **THEN** the platform MUST reject the request with `400` and MUST NOT persist the provider - -### Requirement: Router is injectable and local-development ready -The platform SHALL provide router construction that accepts a core service for tests and future persistence, and a default router that uses the in-memory core service for local development. - -#### Scenario: Local platform process starts -- **WHEN** `cmd/platform` creates the default router -- **THEN** the router MUST expose `/healthz` and all implemented core API routes backed by an in-memory `service.Core` - -#### Scenario: Tests provide a service -- **WHEN** tests call router construction with an explicit `service.Core` -- **THEN** handlers MUST use that service instance for all route operations - -### Requirement: Route catalog matches implemented API surface -The platform route catalog SHALL identify implemented core API routes and clearly distinguish deferred run transport, log ingest, artifact chunk, plugin bridge, and AI invocation behavior. - -#### Scenario: Contributor inspects API catalog -- **WHEN** a contributor opens `platform/api/routes.md` -- **THEN** the file MUST list the implemented create, list, and detail routes and MUST identify deferred behavior as not implemented by this change - -### Requirement: API handler tests verify surface behavior -The platform SHALL include API tests covering successful create/list/detail workflows, JSON error mapping, dependency validation, duplicate handling, missing resources, and AI provider redaction. - -#### Scenario: Platform API tests run -- **WHEN** `go test ./...` is executed inside `platform/` -- **THEN** tests MUST verify the implemented HTTP API behavior without external services or a database diff --git a/openspec/changes/implement-platform-api-surface/tasks.md b/openspec/changes/implement-platform-api-surface/tasks.md deleted file mode 100644 index c056a6d..0000000 --- a/openspec/changes/implement-platform-api-surface/tasks.md +++ /dev/null @@ -1,30 +0,0 @@ -## 1. DTO And Router Contracts - -- [x] 1.1 Add named DTO list/error response contracts and request-to-domain conversion helpers for core resource create requests. -- [x] 1.2 Add injectable platform router construction that wires health and core API routes through `service.Core` with an in-memory default. - -## 2. Core API Handlers - -- [x] 2.1 Implement users, AI providers, game plugins, server instances, and run endpoints create/list/detail handlers. -- [x] 2.2 Implement jobs, artifacts, log streams, and audit events create/list/detail handlers. -- [x] 2.3 Implement shared JSON decode, encode, method, and error mapping behavior using named DTO responses. -- [x] 2.4 Update `platform/api/routes.md` to identify implemented routes and deferred transport/bridge behavior. - -## 3. API Tests - -- [x] 3.1 Add handler tests for successful create/list/detail workflows and query filters. -- [x] 3.2 Add handler tests for malformed JSON, validation errors, duplicates, missing resources, dependency failures, and AI provider redaction. - -## 4. Verification - -- [x] 4.1 Run `go test ./...` from `platform/` and record evidence. -- [x] 4.2 Run `scripts/check-structure.sh` and record evidence. -- [x] 4.3 Run `openspec validate implement-platform-api-surface --strict` and record evidence. - -## Evidence - -- `go test ./api`: passed. -- `go test ./dto`: passed. -- `go test ./...` from `platform/`: passed. -- `scripts/check-structure.sh`: passed. -- `openspec validate implement-platform-api-surface --strict`: passed. diff --git a/openspec/changes/implement-platform-core-domain/.openspec.yaml b/openspec/changes/implement-platform-core-domain/.openspec.yaml deleted file mode 100644 index 8e26fbe..0000000 --- a/openspec/changes/implement-platform-core-domain/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-02 diff --git a/openspec/changes/implement-platform-core-domain/design.md b/openspec/changes/implement-platform-core-domain/design.md deleted file mode 100644 index a535b10..0000000 --- a/openspec/changes/implement-platform-core-domain/design.md +++ /dev/null @@ -1,79 +0,0 @@ -## Context - -`platform/` currently contains the development baseline: a Go module, config loader, health route, route catalog, and markdown contracts for platform resources. The bootstrap architecture requires fixed backend directories for domain types, DTOs, database models, repositories, services, validators, protocol contracts, routes, and shared helpers. Later changes will implement HTTP handlers, run registration, job channels, logs, artifacts, plugin registry, and frontend workflows; those changes need stable core platform types first. - -This change stays inside `platform/` and converts the markdown resource contracts into Go packages with unit-tested in-memory behavior. It does not introduce a database driver or full API handler surface. - -## Goals / Non-Goals - -**Goals:** - -- Define typed Go domain resources for users, game management plugins, server instances, AI providers, run endpoints, jobs, artifacts, log streams, and audit events. -- Define DTO and database-model contracts in dedicated packages so future handlers and persistence work do not invent structs locally. -- Add repository interfaces and an in-memory implementation for deterministic unit tests and early service composition. -- Add services that enforce core invariants for plugin installation metadata, server creation, AI provider redaction, job idempotency, artifacts, logs, and audit. -- Add validation helpers with precise errors for required IDs, enum values, relationships, capability compatibility, redaction, sequence cursors, and bounded summaries. -- Update route catalog documentation with resource contract routes, while leaving handler implementation for a later API-surface change. - -**Non-Goals:** - -- No authentication, sessions, role authorization engine, or password storage. -- No SQL database, migrations, ORM, or external persistence dependency. -- No full HTTP CRUD handlers beyond existing health behavior. -- No run control/job/log/artifact transport implementation. -- No plugin manifest registry implementation or plugin page bridge implementation. -- No raw AI key exposure, direct plugin-to-run access, billing, cloud host sales, or unrelated marketplace behavior. - -## Decisions - -### Decision 1: Domain package owns business vocabulary - -`platform/domain` will define resource structs, enum-like string types, lifecycle constants, filter structs, and copy helpers. Services, repositories, DTOs, and models will reference this vocabulary instead of redefining resource shapes. - -Alternative considered: defining separate shapes independently in every package. Rejected because this would recreate the drift the architecture bootstrap is trying to avoid. - -### Decision 2: DTO and model packages are explicit projections - -DTO structs will represent API request/response boundaries and must not include raw AI provider secrets. Model structs will represent future database tables with JSON/database tags plus `TableName()` methods. Conversion functions will make differences explicit. - -Alternative considered: reusing domain structs directly as API and database structs. Rejected because API redaction and database mapping concerns need independent contracts. - -### Decision 3: Repository interfaces live with the in-memory implementation - -`platform/repo` will define `Store` and typed repository interfaces, plus an in-memory `MemoryStore`. The store will deep-copy resources on read/write and enforce duplicate IDs. This gives services a realistic boundary without committing to SQL in this change. - -Alternative considered: package-level maps in services. Rejected because it hides persistence contracts inside orchestration logic and makes future database replacement harder. - -### Decision 4: Services own cross-resource invariants - -Validators will check local resource validity. Services will enforce cross-resource rules such as “server instances require an installed plugin” and “run endpoint capabilities must satisfy plugin requirements.” Job creation will use an idempotency key to return an existing job for duplicate requests. - -Alternative considered: repositories enforcing all invariants. Rejected because repositories should guard storage integrity while service use cases should own platform behavior. - -### Decision 5: No database or HTTP framework dependency yet - -This change uses only the Go standard library. SQL, migrations, and API handler frameworks are deferred until changes that explicitly implement persistence and API surface behavior. - -Alternative considered: adding SQLite or an HTTP framework now. Rejected because it would widen scope beyond the core domain foundation and complicate verification before handlers exist. - -## Risks / Trade-offs - -- [Risk] In-memory repositories can drift from future SQL behavior. Mitigation: keep interfaces small, copy-on-read/write, and test behavior that future implementations must preserve. -- [Risk] Domain structs may need fields added by later run/log/plugin changes. Mitigation: include the bootstrap resource fields now and allow additive changes through future OpenSpec deltas. -- [Risk] DTO/model projections add boilerplate. Mitigation: keep conversion helpers straightforward and limited to core resources. -- [Risk] Services may look broad before API handlers exist. Mitigation: expose focused methods only for current core workflows and leave transport-specific behavior to later changes. - -## Migration Plan - -1. Add platform domain, DTO, model, validator, repository, and service code behind new unit tests. -2. Keep existing health route behavior unchanged. -3. Update route and resource contract documentation to reference the implemented core resources. -4. Verify with platform unit tests, `scripts/check-structure.sh`, and strict OpenSpec validation. - -Rollback before dependent changes is file removal for the new platform packages and this OpenSpec change. After later API or persistence changes depend on these packages, rollback must follow a new OpenSpec change. - -## Open Questions - -- Which persistent database implementation should replace `MemoryStore` first? -- Which authentication and authorization model should own user/session behavior? -- Which route handlers from the core route catalog should be implemented first in `implement-platform-api-surface`? diff --git a/openspec/changes/implement-platform-core-domain/proposal.md b/openspec/changes/implement-platform-core-domain/proposal.md deleted file mode 100644 index db5bee1..0000000 --- a/openspec/changes/implement-platform-core-domain/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The platform backend currently has architecture contracts and a health endpoint, but the core platform resources are only described in markdown. This change turns those contracts into typed, validated Go domain foundations so later API, run, frontend, and plugin work can depend on stable platform behavior. - -## What Changes - -- Add typed platform domain resources for users, game management plugins, server instances, AI providers, run endpoints, jobs, artifacts, log streams, and audit events. -- Add DTO and model contracts for those resources in their required directories. -- Add repository interfaces plus an in-memory repository implementation suitable for unit tests and early service wiring. -- Add service interfaces and implementations for core create/list/get workflows and lifecycle-safe state changes. -- Add validators for identity, enum values, plugin-to-server relationships, run capability compatibility, AI provider redaction constraints, job idempotency, artifact metadata, log stream cursors, and audit summaries. -- Extend route catalog documentation with the core resource contract surface, without implementing full HTTP handlers in this change. - -## Capabilities - -### New Capabilities -- `platform-core-domain`: Typed backend domain, DTO, model, repository, service, validation, and route-contract foundations for core platform resources. - -### Modified Capabilities -- None. - -## Impact - -- Affects `platform/` only. -- Adds Go packages under `platform/domain`, `platform/dto`, `platform/model`, `platform/repo`, `platform/service`, and `platform/validator`. -- Updates `platform/api/routes.md` and platform markdown contracts where needed to reflect the implemented route contract surface. -- Adds focused platform unit tests for validators, repository behavior, service behavior, and model mappings. -- Does not add billing, cloud host sales, plugin-to-run direct access, raw AI key exposure, or full API handler behavior. diff --git a/openspec/changes/implement-platform-core-domain/specs/platform-core-domain/spec.md b/openspec/changes/implement-platform-core-domain/specs/platform-core-domain/spec.md deleted file mode 100644 index 1b90628..0000000 --- a/openspec/changes/implement-platform-core-domain/specs/platform-core-domain/spec.md +++ /dev/null @@ -1,85 +0,0 @@ -## ADDED Requirements - -### Requirement: Core platform resources are typed -The platform SHALL define typed domain resources for users, AI providers, game management plugins, server instances, run endpoints, jobs, artifacts, log streams, and audit events in the platform domain package. - -#### Scenario: Domain resource definitions are available -- **WHEN** platform services, repositories, DTOs, or models need a core platform resource -- **THEN** they MUST reference named domain resource types instead of defining business structs inside handlers or functions - -#### Scenario: Lifecycle values are centralized -- **WHEN** code validates resource status, state, result, or provider kind values -- **THEN** it MUST use centralized domain constants for the allowed values - -### Requirement: API and database contracts are separated from business logic -The platform SHALL provide named DTO and model structs for core resources in dedicated packages, and model structs SHALL expose explicit table names and database tags. - -#### Scenario: API response contract is needed -- **WHEN** a later API handler returns a core resource -- **THEN** the response shape MUST be available as a named DTO and MUST NOT be declared inside the handler - -#### Scenario: Database model contract is needed -- **WHEN** a future migration or repository references a core resource table -- **THEN** the table mapping MUST be available as a named model with tags and an explicit table name function - -### Requirement: AI provider contracts redact secrets -The platform SHALL store AI provider secret references but MUST NOT expose raw provider API keys through domain responses, DTO responses, services, or plugin-facing contracts. - -#### Scenario: AI provider is returned by service -- **WHEN** an AI provider is created or fetched through the core service layer -- **THEN** the returned provider MUST include an API key reference only and MUST NOT include raw key material - -#### Scenario: AI provider validation runs -- **WHEN** an AI provider uses a direct or relay endpoint -- **THEN** validation MUST require a key reference and redaction policy while rejecting raw secret values in API contract fields - -### Requirement: Core validators enforce resource invariants -The platform SHALL validate required IDs, display names, enum values, bounded lists, server/plugin/run relationships, job idempotency keys, artifact checksums, log cursors, and audit summaries before services persist resources. - -#### Scenario: Invalid core resource is submitted -- **WHEN** a resource has a missing ID, invalid enum value, missing required relationship, unsupported capability, or unbounded summary -- **THEN** validation MUST return a clear error and the service MUST NOT persist the resource - -#### Scenario: Server creation is requested -- **WHEN** a server instance is created from a game management plugin -- **THEN** validation MUST require an installed plugin, a non-deleted server state, a run endpoint, and capability compatibility - -### Requirement: Repository contracts support deterministic core storage -The platform SHALL expose repository interfaces for core resources and an in-memory implementation that supports create, get, list, update, and idempotent job lookup behavior. - -#### Scenario: Duplicate resource is created -- **WHEN** a repository create operation receives an ID that already exists -- **THEN** it MUST return a duplicate error and MUST NOT replace the existing resource - -#### Scenario: Stored resource is read and mutated by caller -- **WHEN** a caller mutates a value returned by the in-memory repository -- **THEN** the stored resource MUST remain unchanged unless an explicit update operation succeeds - -### Requirement: Core services enforce cross-resource workflows -The platform SHALL provide services that compose repositories and validators for core user, plugin, server instance, AI provider, run endpoint, job, artifact, log stream, and audit workflows. - -#### Scenario: Server instance is created from an installed plugin -- **WHEN** a service request names an installed game management plugin and an online or degraded run endpoint with all required capabilities -- **THEN** the service MUST persist a server instance linked to that plugin and run endpoint - -#### Scenario: Server instance creation uses invalid dependencies -- **WHEN** a service request names a disabled or invalid plugin, missing plugin, missing run endpoint, disabled run endpoint, or run endpoint without required capabilities -- **THEN** the service MUST reject the request and MUST NOT persist the server instance - -#### Scenario: Duplicate job request is submitted -- **WHEN** a job create request repeats an existing run endpoint and idempotency key pair -- **THEN** the service MUST return the existing job instead of creating a second job - -### Requirement: Route catalog exposes core resource contract groups -The platform SHALL document route groups for core resources before full API handlers are implemented. - -#### Scenario: Contributor inspects platform API contracts -- **WHEN** a contributor opens the platform route catalog -- **THEN** it MUST list core resource route groups and the DTO contracts those future handlers will use - -### Requirement: Platform core unit tests verify the domain foundation -The platform SHALL include unit tests covering validation, in-memory repository behavior, service invariants, DTO redaction, and model table mappings. - -#### Scenario: Platform tests run -- **WHEN** `go test ./...` is executed inside `platform/` -- **THEN** the tests MUST verify core domain behavior without external services or a database diff --git a/openspec/changes/implement-platform-core-domain/tasks.md b/openspec/changes/implement-platform-core-domain/tasks.md deleted file mode 100644 index e64eadd..0000000 --- a/openspec/changes/implement-platform-core-domain/tasks.md +++ /dev/null @@ -1,34 +0,0 @@ -## 1. Domain Contracts - -- [x] 1.1 Implement typed domain resources, enum constants, filters, and copy helpers for users, AI providers, game plugins, server instances, run endpoints, jobs, artifacts, log streams, and audit events. -- [x] 1.2 Implement named DTO request/response contracts with AI provider redaction helpers for core resources. -- [x] 1.3 Implement database model contracts with JSON/database tags, table-name mappings, and domain conversion helpers for core resources. - -## 2. Validation And Storage - -- [x] 2.1 Implement validator rules and tests for IDs, enum values, AI redaction constraints, plugin/server/run compatibility, job idempotency, artifacts, logs, and audit summaries. -- [x] 2.2 Implement repository interfaces and an in-memory repository with duplicate detection, copy-on-read/write behavior, list/get/update methods, and idempotent job lookup. - -## 3. Services And Contracts - -- [x] 3.1 Implement service interfaces and core service methods for create/list/get workflows across users, AI providers, game plugins, run endpoints, server instances, jobs, artifacts, log streams, and audit events. -- [x] 3.2 Enforce service-level cross-resource invariants for server creation, AI provider redaction, disabled resources, run capability compatibility, and duplicate job idempotency. -- [x] 3.3 Update platform route/resource contract documentation to reference the implemented DTO/domain contracts without adding full HTTP handlers. - -## 4. Verification - -- [x] 4.1 Run platform unit tests with `go test ./...` from `platform/` and record evidence. -- [x] 4.2 Run `scripts/check-structure.sh` and record evidence. -- [x] 4.3 Run `openspec validate implement-platform-core-domain --strict` and record evidence. - -## Evidence - -- `go test ./domain`: passed. -- `go test ./dto`: passed. -- `go test ./model`: passed. -- `go test ./validator`: passed. -- `go test ./repo`: passed. -- `go test ./service`: passed. -- `go test ./...` from `platform/`: passed. -- `scripts/check-structure.sh`: passed. -- `openspec validate implement-platform-core-domain --strict`: passed. diff --git a/openspec/changes/implement-platform-mediated-ai-invocation/.openspec.yaml b/openspec/changes/implement-platform-mediated-ai-invocation/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-platform-mediated-ai-invocation/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-platform-mediated-ai-invocation/design.md b/openspec/changes/implement-platform-mediated-ai-invocation/design.md deleted file mode 100644 index b001a90..0000000 --- a/openspec/changes/implement-platform-mediated-ai-invocation/design.md +++ /dev/null @@ -1,67 +0,0 @@ -## Context - -AI provider credentials and base URLs belong to `platform/`, while plugin pages may request AI assistance only through platform-mediated capabilities. AI-suggested config changes must be reviewable before any run-side write job is dispatched. This change introduces the invocation boundary and keeps provider clients mockable so implementation and tests do not require real keys or external services. - -## Goals / Non-Goals - -**Goals:** - -- Add AI invocation request/response contracts with plugin, server, purpose, model preference, input, and review context. -- Enforce allowed purposes from plugin manifest metadata and platform policy. -- Route invocation through platform-owned provider configuration and a provider client interface. -- Return bounded recommendations, safe text, structured diff suggestions, and usage metadata. -- Ensure config-writing suggestions remain reviewable and are not automatically dispatched to run. -- Add tests for validation, provider selection, mock invocation, purpose denial, redaction, and bridge integration. - -**Non-Goals:** - -- No real external provider network calls in tests or default local mode. -- No raw API key exposure to plugins, platform_web, run, logs, job payloads, or API responses. -- No automatic config write dispatch from AI output. -- No provider billing, agent-provider marketplace, cloud host sales, or unrelated SaaS workflows. - -## Decisions - -### Decision 1: Provider client is an interface with mock default for tests - -The platform service owns provider selection and calls a narrow provider client interface. Tests and local verification use a deterministic fake provider client, while live provider clients can be added later behind the same interface. - -Alternative considered: implement live OpenAI/Anthropic calls immediately. Rejected because this request must not require real keys/accounts or external paid services. - -### Decision 2: AI purposes are mandatory - -Every invocation request includes a purpose such as config recommendation, troubleshooting, log summary, or plugin assistant. Platform validation checks that the plugin and requested context allow that purpose before provider selection. - -Alternative considered: infer purpose from prompt text. Rejected because permission checks need explicit reviewable inputs. - -### Decision 3: Config outputs are recommendations, not writes - -For config-related requests, responses may include a proposed diff or recommendation object. The caller must still use config preview/approval APIs before any run-side write occurs. - -Alternative considered: let AI invocation directly queue config write jobs. Rejected because AI-suggested changes must be reviewable before dispatch. - -### Decision 4: Redaction happens before persistence and response - -Request metadata, prompts, provider errors, and responses are scanned for unsafe credential-like content before logging or returning to browser/plugin callers. - -Alternative considered: rely on caller discipline and avoid scanning. Rejected because provider and prompt output can accidentally include sensitive-looking material. - -## Risks / Trade-offs - -- [Risk] Mock provider behavior can hide live provider quirks. Mitigation: keep provider interface small and add live integration in a separate opt-in change. -- [Risk] Purpose checks may reject useful flows. Mitigation: add new purposes through explicit manifest and OpenSpec updates. -- [Risk] AI output can be over-trusted by operators. Mitigation: config changes return reviewable diffs and never auto-dispatch. - -## Migration Plan - -1. Add platform invocation contracts, validators, provider client interface, service, routes, and tests. -2. Add frontend API client and plugin bridge request plumbing. -3. Add plugin SDK/example helpers and tests. -4. Update docs and run full verification. - -Rollback removes invocation routes/provider interface integrations and this change's artifacts before plugin workflows depend on them. - -## Open Questions - -- Which live provider client should be implemented first after mock-mediated invocation passes? -- Which audit event schema should capture AI recommendation review and operator approval? diff --git a/openspec/changes/implement-platform-mediated-ai-invocation/proposal.md b/openspec/changes/implement-platform-mediated-ai-invocation/proposal.md deleted file mode 100644 index 1e0aa1f..0000000 --- a/openspec/changes/implement-platform-mediated-ai-invocation/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -AI provider management can store safe provider metadata, and plugin bridge contracts can request AI assistance by purpose. The missing piece is the platform-mediated invocation path: plugins and pages need AI help for reviewable recommendations without ever receiving raw provider keys, base URL credentials, or unmanaged model access. - -## What Changes - -- Add platform AI invocation domain, DTO, validator, service, and API behavior for purpose-scoped requests. -- Route requests through platform-owned provider configuration and mockable provider clients, with no real-key requirement for tests. -- Return bounded AI recommendations, usage metadata, and reviewable config diff suggestions instead of direct run-side writes. -- Add plugin bridge/SDK and frontend client integration for `ai.invoke` requests without exposing provider credentials. -- Add tests proving purpose enforcement, provider redaction, unsafe prompt/payload rejection, mock provider behavior, and no raw key exposure. - -## Capabilities - -### New Capabilities - -- `platform-mediated-ai-invocation`: Platform-owned AI invocation for plugin and console workflows with purpose validation, credential isolation, bounded outputs, and reviewable recommendations. - -### Modified Capabilities - -- Builds on `ai-provider-management` and plugin bridge capabilities without adding raw provider access to plugins or platform_web. - -## Impact - -- Affects `platform/` AI invocation contracts, services, validators, APIs, and tests. -- Affects `platform_web/` API contracts/client and plugin bridge host behavior for AI requests. -- Affects `plugins/` SDK/example AI request helpers and tests. -- Does not require real provider keys/accounts, external paid services, live network calls in tests, billing, cloud host sales, or direct config writes. diff --git a/openspec/changes/implement-platform-mediated-ai-invocation/specs/platform-mediated-ai-invocation/spec.md b/openspec/changes/implement-platform-mediated-ai-invocation/specs/platform-mediated-ai-invocation/spec.md deleted file mode 100644 index 9df94e0..0000000 --- a/openspec/changes/implement-platform-mediated-ai-invocation/specs/platform-mediated-ai-invocation/spec.md +++ /dev/null @@ -1,65 +0,0 @@ -## ADDED Requirements - -### Requirement: AI invocation is platform-mediated - -The platform SHALL expose AI invocation only through platform-owned APIs and services that use stored provider metadata and never expose raw provider credentials to plugins, platform_web, run, or API responses. - -#### Scenario: Plugin invokes allowed AI purpose -- **WHEN** a plugin page submits an `ai.invoke` request with an allowed purpose and bounded input -- **THEN** the platform MUST validate the purpose, select an enabled provider through platform-owned configuration, invoke a provider client, and return a redacted AI response - -#### Scenario: Raw provider credential is never returned -- **WHEN** any AI invocation succeeds or fails -- **THEN** the response MUST NOT include raw API keys, provider bearer tokens, provider base URL secrets, platform auth storage, run credentials, direct sockets, or raw host paths - -### Requirement: AI purposes and payloads are validated - -The platform SHALL validate invocation purpose, plugin permissions, server scope, model preference, input size, context references, and unsafe credential-like content before invoking a provider client. - -#### Scenario: Undeclared purpose is denied -- **WHEN** a plugin requests an AI purpose not declared by its manifest metadata or current bridge page permissions -- **THEN** the platform MUST deny the request before provider invocation - -#### Scenario: Unsafe payload is rejected -- **WHEN** an invocation payload includes raw key-like content, absolute host paths, direct sockets, or unbounded input -- **THEN** the platform MUST reject the request with a safe validation error - -### Requirement: Provider invocation is mockable and bounded - -The platform SHALL invoke AI through a provider client interface that supports deterministic tests without real external accounts or paid services. - -#### Scenario: Mock provider returns recommendation -- **WHEN** tests or local mode use the mock provider client -- **THEN** invocation MUST return deterministic safe content, usage metadata, and optional structured recommendations without network access - -#### Scenario: Provider failure is redacted -- **WHEN** the provider client returns an error -- **THEN** the platform MUST return a safe error response without provider credentials or raw transport details - -### Requirement: Config suggestions remain reviewable - -AI-generated configuration changes SHALL be returned as recommendations or diff previews and SHALL NOT directly dispatch run-side config write jobs. - -#### Scenario: AI suggests config edit -- **WHEN** an invocation purpose requests config assistance -- **THEN** the response MAY include a proposed diff or recommendation, but the platform MUST require the separate config preview/approval workflow before dispatching a write job - -### Requirement: Frontend and plugin SDK use mediated AI contracts - -The frontend and plugin SDK SHALL use typed AI bridge/API contracts and SHALL NOT expose provider keys or raw provider configuration to plugin code. - -#### Scenario: Plugin SDK builds AI request -- **WHEN** plugin code builds an AI invocation request -- **THEN** it MUST include purpose, request ID, scoped input, and context references while excluding raw provider credentials - -#### Scenario: Browser walkthrough verifies AI request safety -- **WHEN** AI invocation UI behavior is claimed complete -- **THEN** a browser walkthrough MUST verify an AI-assisted workflow renders redacted results and does not expose raw credential markers - -### Requirement: Platform-mediated AI invocation is verified - -The change SHALL include backend tests, frontend tests/build, plugin tests/typecheck, browser walkthrough evidence, structure validation, and strict OpenSpec validation. - -#### Scenario: Verification commands pass -- **WHEN** the change is complete -- **THEN** platform tests, platform_web tests/typecheck/build, plugin tests/typecheck, `scripts/check-structure.sh`, and `openspec validate implement-platform-mediated-ai-invocation --strict` MUST pass diff --git a/openspec/changes/implement-platform-mediated-ai-invocation/tasks.md b/openspec/changes/implement-platform-mediated-ai-invocation/tasks.md deleted file mode 100644 index 04e40e8..0000000 --- a/openspec/changes/implement-platform-mediated-ai-invocation/tasks.md +++ /dev/null @@ -1,41 +0,0 @@ -## 1. Platform AI Invocation Contracts - -- [x] 1.1 Add domain and DTO contracts for AI invocation requests, context refs, purposes, recommendations, usage metadata, and safe errors. -- [x] 1.2 Add validators for purpose authorization, provider IDs, model preferences, bounded input/output, context refs, and unsafe credential/path/socket content. -- [x] 1.3 Add a platform provider client interface and deterministic mock provider implementation for tests/local verification. - -## 2. Platform AI Invocation Service And API - -- [x] 2.1 Add service methods that authorize purpose-scoped invocation, select enabled providers, call the provider client, redact outputs, and return typed responses. -- [x] 2.2 Implement AI invocation route using named DTOs and service methods. -- [x] 2.3 Ensure config-related AI responses produce reviewable recommendations/diffs and never dispatch run-side writes directly. -- [x] 2.4 Update platform route/protocol documentation for mediated AI invocation and live-provider deferral. -- [x] 2.5 Add platform tests for allowed invocation, undeclared purpose denial, unsafe payload rejection, provider failure redaction, config recommendation reviewability, and no raw key exposure. - -## 3. Frontend And Plugin Integration - -- [x] 3.1 Add centralized `platform_web/api` AI invocation types and client methods. -- [x] 3.2 Integrate AI invocation into plugin bridge host execution flow for `ai.invoke` responses. -- [x] 3.3 Add plugin SDK/example helpers for AI invocation request builders and safe response parsing. -- [x] 3.4 Add frontend and plugin tests for mediated AI requests, denied purposes, redacted results, and no direct provider config exposure. - -## 4. Verification - -- [x] 4.1 Run `cd platform && go test ./...` and record evidence. -- [x] 4.2 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 4.3 Run `cd plugins && npm run typecheck && npm test` and record evidence. -- [x] 4.4 Run browser walkthrough for mediated AI invocation and record evidence. -- [x] 4.5 Run `scripts/check-structure.sh` and record evidence. -- [x] 4.6 Run `openspec validate implement-platform-mediated-ai-invocation --strict` and record evidence. - -## Evidence - -- 2026-07-06: `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -run TestAIInvocationAPIIsMediatedAndSafe -count=1` passed for mediated invocation, purpose denial, unsafe prompt rejection, config suggestion reviewability/no job dispatch, bridge `ai.invoke`, and no forbidden response fragments. -- 2026-07-06: `cd platform_web && npm run typecheck` and `cd platform_web && npm test -- --run api/client.test.ts utils/pluginBridgeHost.test.ts` passed for AI invocation API types/client and bridge `ai.invoke` dispatcher behavior. -- 2026-07-06: `cd plugins && npm run typecheck` and `cd plugins && npm test -- --run tests/manifest-validation.test.ts` passed for SDK AI invocation request/response helpers and no provider config exposure. -- 2026-07-06: `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./...` passed. -- 2026-07-06: `cd platform_web && npm run typecheck`, `cd platform_web && npm test`, and `cd platform_web && npm run build` passed. -- 2026-07-06: `cd plugins && npm run typecheck` and `cd plugins && npm test` passed. -- 2026-07-06: Browser walkthrough passed using a local mock platform API plus headless Chrome: logged in, opened `#/servers/server-ai-walkthrough`, switched to `插件控制`, clicked `AI 调用`, verified `AI 建议已返回`, and confirmed no forbidden credential/path/provider fragments were rendered. -- 2026-07-06: `scripts/check-structure.sh` passed. -- 2026-07-06: `openspec validate implement-platform-mediated-ai-invocation --strict` passed (`Change 'implement-platform-mediated-ai-invocation' is valid`; PostHog DNS flush warnings were non-fatal telemetry failures). diff --git a/openspec/changes/implement-platform-observability-and-config-read/.openspec.yaml b/openspec/changes/implement-platform-observability-and-config-read/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-platform-observability-and-config-read/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-platform-observability-and-config-read/design.md b/openspec/changes/implement-platform-observability-and-config-read/design.md deleted file mode 100644 index bde1470..0000000 --- a/openspec/changes/implement-platform-observability-and-config-read/design.md +++ /dev/null @@ -1,62 +0,0 @@ -## Context - -The current frontend calls `/metrics/platform`, `/metrics/server-instances`, and `/server-instances/{id}/config`, but the backend router does not implement them. HomePage falls back to partial data or error states, and ServerDetailPage uses a hardcoded `server.properties` sample when config read fails. - -This change is deliberately read-only. It creates the observability/config read surface needed by later config write, AI suggestion, and run execution changes without introducing file mutation or process orchestration. - -## Goals / Non-Goals - -**Goals:** - -- Implement platform resource usage and per-server metrics API routes. -- Implement safe server config read route with role-scoped access. -- Keep response contracts bounded and free of secrets, host paths, direct sockets, and raw run credentials. -- Update frontend pages to consume real API data and demote local samples to explicit fallback. -- Add service/API/frontend tests and browser walkthrough evidence. - -**Non-Goals:** - -- No config write, file write, or diff approval routes. -- No external metrics collector or long-term metrics storage backend. -- No browser log tail, artifact transfer UI, or AI log analysis. -- No run worker changes beyond existing data sources. - -## Decisions - -### Decision 1: Keep metrics as platform-owned read models - -The service will expose platform and server metrics through platform DTOs. Initial values may be derived from existing run endpoint/server/job metadata or stored in the in-memory repository, but callers see stable read contracts. - -Alternative considered: let the frontend compute all metrics locally from server lists. Rejected because the console already has API client methods and future run workers need a platform-owned metrics surface. - -### Decision 2: Server config read returns logical content only - -The config read response returns server instance ID, config version, content, format/key metadata, and timestamps. It must not include host filesystem paths or run-local socket details. - -Alternative considered: return a host path for browser editing. Rejected because run paths must not leak to platform_web or plugins. - -### Decision 3: Role-scoped access applies to config and server metrics - -Platform administrators can read all metrics/config. Server owners and administrators can read only their server instances. This reuses the existing bearer session and ACL behavior. - -### Decision 4: Frontend fallback remains visibly non-production - -Local sample config can remain only as an explicit fallback state for API-unavailable development/demo flows. Production rendering must prefer API data and show errors/empty states honestly. - -## Risks / Trade-offs - -- [Risk] Derived metrics can look less live than future run telemetry. Mitigation: expose source/timestamp fields and keep later real-time collectors as a separate change. -- [Risk] Config content may be stale relative to actual files until run worker integration exists. Mitigation: include config version and source metadata. -- [Risk] Server detail can still show fallback config if backend is unavailable. Mitigation: label fallback clearly and add tests that API success suppresses fallback. - -## Migration Plan - -1. Add metrics/config domain, DTO, validators, and service methods in `platform/`. -2. Add API handlers/routes and route documentation updates. -3. Update `platform_web` API types/views to render API metrics/config. -4. Add tests and browser walkthrough. - -## Open Questions - -- Whether future metrics persistence should be a repository table or a projection from run heartbeats/logs. -- Whether config format should start as plain text only or include structured sections after file dispatch is implemented. diff --git a/openspec/changes/implement-platform-observability-and-config-read/proposal.md b/openspec/changes/implement-platform-observability-and-config-read/proposal.md deleted file mode 100644 index 0c504c3..0000000 --- a/openspec/changes/implement-platform-observability-and-config-read/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -The console already calls platform metrics and server config read APIs, but those routes are not implemented. Operators see empty/error states or local sample config even when real server, job, and run endpoint data exists. This change closes the read-only observability gap before write and execution work builds on it. - -## What Changes - -- Add backend contracts and routes for platform resource usage, per-server metrics, and server config reads. -- Enforce existing role-scoped server access for server config and server metrics. -- Return bounded, safe metadata without host paths, raw credentials, run sockets, or AI keys. -- Update HomePage and ServerDetailPage to prefer API data and keep any local samples as explicit dev/demo fallback only. -- Add tests and browser walkthrough coverage for 首页 and server detail config views. - -## Capabilities - -### New Capabilities - -- `platform-observability-and-config-read`: API-backed platform/server metrics and safe server config read workflows. - -### Modified Capabilities - -- `platform-web-console-shell`: Removes production reliance on local observability/config samples for existing console pages. - -## Impact - -- Affects `platform/` domain, DTO, validators, service, API handlers, route docs, and tests. -- Affects `platform_web/` API types/client usage, HomePage, ServerDetailPage, tests, and browser walkthrough. -- Does not add config writes, file dispatch, log tail transport, external metrics backends, billing, cloud host sales, raw host paths, raw credentials, or direct run access. diff --git a/openspec/changes/implement-platform-observability-and-config-read/specs/platform-observability-and-config-read/spec.md b/openspec/changes/implement-platform-observability-and-config-read/specs/platform-observability-and-config-read/spec.md deleted file mode 100644 index 0f17b06..0000000 --- a/openspec/changes/implement-platform-observability-and-config-read/specs/platform-observability-and-config-read/spec.md +++ /dev/null @@ -1,53 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform exposes resource usage metrics -The platform SHALL expose a bounded platform resource usage endpoint for the management console. - -#### Scenario: Platform metrics loaded -- **WHEN** an authorized platform administrator requests platform resource usage -- **THEN** the platform MUST return CPU, memory, disk, source, and timestamp metadata in a named DTO response - -#### Scenario: Platform metrics remain safe -- **WHEN** the platform returns resource usage data -- **THEN** the response MUST NOT include host paths, raw credentials, direct sockets, storage backend credentials, or raw AI provider keys - -### Requirement: Platform exposes per-server metrics -The platform SHALL expose bounded per-server metrics for server management and overview pages. - -#### Scenario: Server metrics listed -- **WHEN** an authorized user requests server metrics -- **THEN** the platform MUST return only metrics for server instances visible to that user - -#### Scenario: Pending metrics are bounded -- **WHEN** a server does not have current metrics -- **THEN** the platform MUST return a bounded missing/pending representation rather than unsafe fallback internals - -### Requirement: Server config read is safe and role scoped -The platform SHALL expose a server config read endpoint that returns logical config content for an authorized server instance. - -#### Scenario: Owner reads server config -- **WHEN** a server owner requests config for their server instance -- **THEN** the platform MUST return config content, config version, server instance ID, and bounded metadata - -#### Scenario: Unauthorized config read rejected -- **WHEN** a user without access requests server config -- **THEN** the platform MUST reject the request and MUST NOT return config content - -#### Scenario: Config response hides run internals -- **WHEN** the platform returns server config -- **THEN** the response MUST NOT expose run credentials, raw host paths, direct sockets, or raw secret values - -### Requirement: Console uses API-backed observability and config reads -The frontend SHALL prefer API-backed platform metrics, server metrics, and server config content over hardcoded production data. - -#### Scenario: API config suppresses fallback -- **WHEN** the server config API returns content -- **THEN** ServerDetailPage MUST render that content and MUST NOT display the local sample config label - -#### Scenario: Metrics render from API -- **WHEN** metrics APIs return data -- **THEN** HomePage and server cards MUST render API metric values with safe loading/error states - -#### Scenario: Fallback is explicit -- **WHEN** a development fallback is used because an API is unavailable -- **THEN** the UI MUST label it as local/demo fallback and MUST NOT present it as persisted platform data diff --git a/openspec/changes/implement-platform-observability-and-config-read/tasks.md b/openspec/changes/implement-platform-observability-and-config-read/tasks.md deleted file mode 100644 index e0bfd5a..0000000 --- a/openspec/changes/implement-platform-observability-and-config-read/tasks.md +++ /dev/null @@ -1,51 +0,0 @@ -## 1. Platform Metrics Contracts - -- [x] 1.1 Add domain contracts for platform resource usage snapshots and per-server metrics. -- [x] 1.2 Add DTO request/response contracts for `/metrics/platform` and `/metrics/server-instances`. -- [x] 1.3 Add repository/service interfaces for storing or deriving platform and server metrics. -- [x] 1.4 Add validators for metric ranges, timestamps, server IDs, and bounded list responses. - -## 2. Server Config Read Contracts - -- [x] 2.1 Add domain and DTO contracts for server config read responses. -- [x] 2.2 Add service method for reading server config metadata/content by server instance. -- [x] 2.3 Enforce role-scoped access for config reads using existing server ACL rules. -- [x] 2.4 Ensure config read responses never expose host paths, raw credentials, or direct run sockets. - -## 3. Backend API Surface - -- [x] 3.1 Implement `GET /api/v1/metrics/platform`. -- [x] 3.2 Implement `GET /api/v1/metrics/server-instances`. -- [x] 3.3 Implement `GET /api/v1/server-instances/{id}/config`. -- [x] 3.4 Update `platform/api/routes.md` and protocol docs to mark these routes implemented. - -## 4. Frontend Integration - -- [x] 4.1 Update HomePage to render platform metrics from API data instead of empty fallback states. -- [x] 4.2 Update ServerDetailPage config section to show API config content when available. -- [x] 4.3 Remove or clearly isolate hardcoded config fallback from production flow. -- [x] 4.4 Add UI tests for metrics/config loading, errors, and no-secret rendering. - -## 5. Verification - -- [x] 5.1 Add platform service/API tests for metrics and config read access control. -- [x] 5.2 Run `cd platform && go test ./...` and record evidence. -- [x] 5.3 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 5.4 Run browser walkthrough for 首页 and server detail config view. -- [x] 5.5 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.6 Run `openspec validate implement-platform-observability-and-config-read --strict` and record evidence. - -## Evidence - -- 2026-07-06: Added platform domain/DTO/service/validator/API implementation for platform metrics, server metrics, and safe server config reads. -- 2026-07-06: Added platform service/API tests for role-scoped metrics and config reads, unauthorized access denial, and no host path/raw credential/socket fragments. -- 2026-07-06: Updated frontend API contracts/client tests for platform metrics, server metrics, and server config responses; existing HomePage and ServerDetailPage API flows consume these methods with explicit local fallback labeling. -- 2026-07-06: `cd platform && go test ./domain ./dto ./validator ./service ./api` passed. -- 2026-07-06: `cd platform_web && npm test -- --run api/client.test.ts pages/ConsolePages.test.tsx` passed with 2 files / 9 tests. -- 2026-07-06: `cd platform && go test ./...` passed across api, cmd/platform, config, domain, dto, model, repo, service, and validator packages. -- 2026-07-06: `cd platform_web && npm run typecheck` passed. -- 2026-07-06: `cd platform_web && npm test` passed with 9 files / 31 tests. -- 2026-07-06: `cd platform_web && npm run build` passed and produced Vite production assets. -- 2026-07-06: Headless Chrome walkthrough against `http://127.0.0.1:5175/` passed: seeded platform API data, verified 首页 rendered API platform metrics/resource usage and server distribution, opened `#/servers/server-walkthrough`, verified config tab showed `配置版本 v1` with API textarea content `server.name=Walkthrough SCUM`, and confirmed rendered text/textarea excluded `/Users/`, `unix://`, `Bearer `, `sk-`, and `password=`. -- 2026-07-06: `scripts/check-structure.sh` passed with `structure check passed`. -- 2026-07-06: `openspec validate implement-platform-observability-and-config-read --strict` passed with `Change 'implement-platform-observability-and-config-read' is valid`. diff --git a/openspec/changes/implement-platform-web-console-shell/.openspec.yaml b/openspec/changes/implement-platform-web-console-shell/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-platform-web-console-shell/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-platform-web-console-shell/design.md b/openspec/changes/implement-platform-web-console-shell/design.md deleted file mode 100644 index 0b76d22..0000000 --- a/openspec/changes/implement-platform-web-console-shell/design.md +++ /dev/null @@ -1,71 +0,0 @@ -## Context - -`platform_web/` already contains the required first-party navigation entries and a working AI provider management page. The other first-party areas still use thin metric-only placeholders, and the shell owns only hash replacement without a reusable page model, status surface, or acceptance-focused layout. - -This change keeps implementation inside `platform_web/` and prepares the frontend for later server-management workflows. It uses existing Vite, React, TypeScript, local contracts, and fallback data patterns; it does not add backend behavior or cross-root imports. - -## Goals / Non-Goals - -**Goals:** - -- Make the console shell route-aware, accessible, and stable across refresh/hash navigation. -- Give every required first-party area a useful operational page surface rather than a bare metric placeholder. -- Keep API contracts and page contracts in dedicated frontend directories. -- Add frontend client methods for currently available platform resources used by shell pages. -- Add tests covering route/page behavior and page-level rendering. -- Validate the UI with build/test commands and browser walkthrough evidence. - -**Non-Goals:** - -- Do not implement full server create/start/stop workflows; that is the next queued change. -- Do not add billing, cloud host sales, provider marketplace, or unrelated SaaS behavior. -- Do not expose raw AI keys, run credentials, host paths, storage backend credentials, or direct run sockets. -- Do not introduce a router dependency unless existing hash navigation becomes insufficient. -- Do not change backend platform APIs in this change. - -## Decisions - -### Decision 1: Keep hash routing for the shell baseline - -The app will continue to use hash-based navigation, but route resolution will be centralized in `routes/` and `stores/`. This keeps the console deployable as a static frontend while preserving direct links and refresh behavior. - -Alternative considered: add React Router now. Rejected because the current routing needs are simple and adding a dependency before workflow pages exist would increase surface area without improving the backlog item. - -### Decision 2: Use local page view contracts for first-party pages - -Each page will consume typed view contracts from `contracts/` and local-safe seed data until backend workflow APIs exist. Page components remain focused on rendering and local interactions, not hidden shared business types. - -Alternative considered: place page-specific types inside page components. Rejected because repository rules require frontend page roots to keep view contracts in fixed directories. - -### Decision 3: Extend the frontend API client only for existing backend endpoints - -The frontend client will add typed methods for health, game plugins, server instances, and bridge authorization where backend endpoints already exist. Pages may fall back to local data if APIs are unavailable. - -Alternative considered: create mock-only page data without API client expansion. Rejected because later workflow pages need a stable client surface and tests should exercise named contracts. - -### Decision 4: Design the shell for dense operational scanning - -The shell will prioritize compact navigation, status chips, tables/lists, and action strips over marketing-style presentation. The visual system should remain consistent with existing AI provider management styling. - -Alternative considered: add a landing-page style dashboard. Rejected because this is an operational game server management console, not a product marketing site. - -## Risks / Trade-offs - -- [Risk] Pages can imply workflows that are not implemented yet. Mitigation: label actions as planning/local status and avoid dispatching run jobs. -- [Risk] Local fallback data can drift from backend contracts. Mitigation: keep frontend API types explicit and covered by tests. -- [Risk] Hash routing can become limiting later. Mitigation: centralize route parsing so a future router can replace the implementation without changing page contracts. -- [Risk] A richer shell may introduce responsive layout regressions. Mitigation: run build/tests and a browser walkthrough at desktop and mobile widths. - -## Migration Plan - -1. Add console shell view contracts, route helpers, API types/client methods, and tests. -2. Expand first-party pages with operational sections and local-safe fallback data. -3. Update shell styling for desktop and mobile responsive navigation/content. -4. Run frontend tests/build/typecheck, structure validation, strict OpenSpec validation, and browser walkthrough. - -Rollback is contained to `platform_web/` and this OpenSpec change: remove the new page contracts/client methods/page rendering changes and return to the previous placeholder shell. - -## Open Questions - -- Whether a later workflow change should replace hash routing with a full router after server management forms and nested pages are introduced. -- Whether dashboard summaries should eventually come from a dedicated backend summary endpoint or compose existing list endpoints. diff --git a/openspec/changes/implement-platform-web-console-shell/proposal.md b/openspec/changes/implement-platform-web-console-shell/proposal.md deleted file mode 100644 index f0c4ea8..0000000 --- a/openspec/changes/implement-platform-web-console-shell/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -The management frontend has the required first-party navigation and a deeper AI provider page, but the console shell still behaves like a static page switcher with placeholder pages. This change turns `platform_web/` into a durable management console foundation for the next server-management workflows. - -## What Changes - -- Add a route-aware console shell with stable navigation state, page metadata, and accessible active-page behavior. -- Add richer first-party page surfaces for 首页、服务器管理、插件市场、用户管理、AI 提供商管理 using local-safe data until backend workflow APIs are introduced. -- Add frontend API types/client methods needed by shell pages for game plugins, server instances, and health/status summaries without exposing raw credentials or run internals. -- Add page/view contracts, schemas, and utilities that keep page components free of hidden shared business types. -- Add route/page tests, frontend build coverage, and a browser walkthrough for the console shell. - -## Capabilities - -### New Capabilities - -- `platform-web-console-shell`: Defines the first-party frontend console shell, navigation behavior, page contracts, API client surface, and visual acceptance expectations for the management console. - -### Modified Capabilities - -- None. - -## Impact - -- `platform_web/`: app shell, route definitions, page registry, page contracts, API types/client methods, schemas/utilities, first-party pages, tests, and styling. -- OpenSpec artifacts and validation for the new `platform-web-console-shell` capability. -- No backend behavior changes are required in this change; pages may use existing API endpoints with local fallback data. diff --git a/openspec/changes/implement-platform-web-console-shell/specs/platform-web-console-shell/spec.md b/openspec/changes/implement-platform-web-console-shell/specs/platform-web-console-shell/spec.md deleted file mode 100644 index 726ba08..0000000 --- a/openspec/changes/implement-platform-web-console-shell/specs/platform-web-console-shell/spec.md +++ /dev/null @@ -1,91 +0,0 @@ -## ADDED Requirements - -### Requirement: Console shell supports first-party route navigation - -The frontend SHALL provide a route-aware console shell for 首页、服务器管理、插件市场、用户管理、AI 提供商管理 with stable active-page state and refresh-safe URL hash handling. - -#### Scenario: User opens a first-party route hash - -- **WHEN** the browser opens the app with a hash for a known first-party page -- **THEN** the shell renders that page and marks the corresponding navigation item as current - -#### Scenario: User selects navigation item - -- **WHEN** the user selects a first-party navigation item -- **THEN** the shell updates the active page and URL hash without a full page reload - -#### Scenario: User opens unknown route hash - -- **WHEN** the browser opens the app with an unknown hash -- **THEN** the shell falls back to 首页 without rendering an invalid page - -### Requirement: First-party pages provide operational surfaces - -The frontend SHALL render useful operational surfaces for every required first-party area using typed local view contracts and safe fallback data. - -#### Scenario: Home page renders platform overview - -- **WHEN** 首页 is active -- **THEN** the page shows platform health, server/plugin/user/provider summary sections, and next-action context without exposing secrets - -#### Scenario: Server management page renders instance overview - -- **WHEN** 服务器管理 is active -- **THEN** the page shows server instance status, run endpoint context, lifecycle action affordances, and pending job/log indicators without dispatching unimplemented workflows - -#### Scenario: Plugin marketplace page renders installed plugin catalog - -- **WHEN** 插件市场 is active -- **THEN** the page shows installed plugin metadata, permissions, bridge readiness, and validation status without commerce or cloud-host sales features - -#### Scenario: User management page renders user and role overview - -- **WHEN** 用户管理 is active -- **THEN** the page shows user status, role assignment context, and access-review indicators using frontend-owned contracts - -### Requirement: Frontend API client exposes shell resource contracts - -The frontend SHALL define API types and client methods for shell pages using existing platform API endpoints and without casual cross-root imports. - -#### Scenario: Client lists game plugins - -- **WHEN** shell code requests game plugin data -- **THEN** the API client uses a named frontend contract for `/api/v1/game-plugins` - -#### Scenario: Client lists server instances - -- **WHEN** shell code requests server instance data -- **THEN** the API client uses a named frontend contract for `/api/v1/server-instances` - -#### Scenario: Client authorizes plugin bridge action - -- **WHEN** shell code requests bridge authorization -- **THEN** the API client uses a named frontend contract for `/api/v1/plugin-bridge/authorize` and does not include raw credentials - -### Requirement: Console shell is testable and visually accepted - -The frontend SHALL include route/page tests, pass TypeScript/build validation, and complete a browser walkthrough before the change is complete. - -#### Scenario: Automated tests cover required pages - -- **WHEN** frontend tests run -- **THEN** they verify required navigation entries and representative content for each first-party page - -#### Scenario: Browser walkthrough validates shell usability - -- **WHEN** the browser walkthrough runs against the built or dev frontend -- **THEN** desktop and mobile views show coherent navigation, readable content, and no obvious overlap or blank page - -### Requirement: Console shell preserves security boundaries - -The frontend SHALL not expose raw AI keys, run credentials, host paths, direct sockets, or storage backend credentials in shell pages, API contracts, or fallback data. - -#### Scenario: Page content renders provider credentials - -- **WHEN** AI provider or plugin data includes credential references -- **THEN** the frontend shows only safe references such as `secret://`, `vault://`, or `env://` and never raw key material - -#### Scenario: Plugin bridge context is rendered - -- **WHEN** plugin bridge readiness appears in shell pages -- **THEN** the frontend shows permissions and action readiness without exposing raw run or platform auth internals diff --git a/openspec/changes/implement-platform-web-console-shell/tasks.md b/openspec/changes/implement-platform-web-console-shell/tasks.md deleted file mode 100644 index 7e28ee3..0000000 --- a/openspec/changes/implement-platform-web-console-shell/tasks.md +++ /dev/null @@ -1,40 +0,0 @@ -## 1. Shell Routing and Contracts - -- [x] 1.1 Add route helper contracts for resolving known hashes, fallback routes, and page metadata. -- [x] 1.2 Add shell view contracts for dashboard summaries, server instances, plugins, users, and provider status. -- [x] 1.3 Extend frontend API types and client methods for game plugins, server instances, health, and plugin bridge authorization. - -## 2. First-Party Page Implementation - -- [x] 2.1 Expand 首页 into an operational overview with health, server, plugin, user, and AI provider summary sections. -- [x] 2.2 Expand 服务器管理 into an instance/run/job/log overview without dispatching unimplemented lifecycle workflows. -- [x] 2.3 Expand 插件市场 into an installed plugin catalog with permissions, bridge readiness, and validation state. -- [x] 2.4 Expand 用户管理 into a user/role/access-review overview using frontend-owned contracts. -- [x] 2.5 Keep AI 提供商管理 compatible with the updated shell and shared page contracts. - -## 3. Styling and Responsive Shell - -- [x] 3.1 Update shell/page styling for dense operational layouts, responsive navigation, and accessible active states. -- [x] 3.2 Ensure page content avoids raw secret, host path, direct socket, and run credential display. - -## 4. Tests and Browser Walkthrough - -- [x] 4.1 Add route helper and API client tests for shell resource contracts. -- [x] 4.2 Add page rendering tests for each required first-party page. -- [x] 4.3 Run `cd platform_web && npm test`. -- [x] 4.4 Run `cd platform_web && npm run build`. -- [x] 4.5 Run browser walkthrough for desktop and mobile shell views. - -## 5. Verification - -- [x] 5.1 Run `scripts/check-structure.sh`. -- [x] 5.2 Run `openspec validate implement-platform-web-console-shell --strict`. -- [x] 5.3 Record verification evidence in this task file before marking verification tasks complete. - -## Evidence - -- `cd platform_web && npm test`: passed on 2026-07-03; Vitest reported 6 files and 14 tests passing. -- `cd platform_web && npm run build`: passed on 2026-07-03; TypeScript no-emit and Vite production build completed. -- Browser walkthrough: passed on 2026-07-03 using local Chrome CDP against `http://127.0.0.1:5174/`; checked 首页、服务器管理、插件市场、用户管理、AI 提供商管理 at desktop 1440x1000 and mobile 390x844 with no missing target content or page-level horizontal overflow. Screenshots were written under `/tmp/platform-web-console-shell-walkthrough/`. -- `scripts/check-structure.sh`: passed on 2026-07-03 with `structure check passed`. -- `openspec validate implement-platform-web-console-shell --strict`: passed on 2026-07-03 with `Change 'implement-platform-web-console-shell' is valid`. diff --git a/openspec/changes/implement-plugin-bridge-and-sdk/.openspec.yaml b/openspec/changes/implement-plugin-bridge-and-sdk/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-plugin-bridge-and-sdk/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-plugin-bridge-and-sdk/design.md b/openspec/changes/implement-plugin-bridge-and-sdk/design.md deleted file mode 100644 index 2cc9527..0000000 --- a/openspec/changes/implement-plugin-bridge-and-sdk/design.md +++ /dev/null @@ -1,83 +0,0 @@ -## Context - -The registry change added a validated game plugin manifest and marketplace-ready plugin metadata, but plugin pages still have only documentation-level bridge notes. The next backlog item needs a concrete contract that lets plugin UI request platform abilities without learning platform credentials, run connection details, host paths, storage backends, or AI provider keys. - -The implementation spans three roots: - -- `plugins/` owns the author-facing TypeScript SDK, manifest bridge declarations, and example plugin usage. -- `platform_web/` owns the browser host-side bridge contracts and utility checks used by future plugin pages. -- `platform/` owns the authoritative permission/session validation before privileged bridge requests are accepted. - -## Goals / Non-Goals - -**Goals:** - -- Define a typed bridge request/response contract for plugin pages. -- Enforce bridge actions against manifest-declared plugin permissions and AI purposes. -- Provide SDK helpers for plugin authors to check permissions and build safe bridge requests. -- Add platform validators/services/API routes for bridge session and action authorization. -- Keep plugin pages mediated by platform APIs instead of direct run, socket, host path, credential, artifact storage, or AI provider access. -- Add tests proving forbidden bridge actions fail and allowed scoped actions pass. - -**Non-Goals:** - -- Do not implement plugin iframe loading or a complete marketplace UI in this change. -- Do not execute plugin lifecycle actions or server workflows end to end. -- Do not expose raw AI provider keys, run credentials, direct sockets, raw host paths, or storage backend endpoints. -- Do not add billing, cloud host sales, provider marketplace, or unrelated SaaS marketplace behavior. -- Do not introduce cross-root runtime imports; mirrored contracts remain explicit at ownership boundaries. - -## Decisions - -### Decision 1: Bridge permissions are evaluated from manifest metadata - -The platform will authorize bridge actions from the installed plugin metadata produced by manifest registration. Requests include plugin ID, route key, optional server instance ID, action name, and purpose-specific payload metadata. The platform checks that the requested action maps to declared plugin permissions and AI purposes before returning an allowed decision. - -Alternative considered: trust the browser SDK to decide permission outcomes. Rejected because plugin UI code is not a security boundary and can be modified by authors or users. - -### Decision 2: Use narrow bridge action names instead of generic RPC - -The bridge contract will model first-party action names such as `server.instances.read`, `jobs.dispatch`, `logs.query`, `artifacts.open`, `files.request`, and `ai.invoke`. Each action maps to specific permission requirements. - -Alternative considered: expose a generic `api.request` bridge that proxies arbitrary platform paths. Rejected because arbitrary request forwarding makes permission reviews harder and risks exposing unrelated platform APIs to plugin pages. - -### Decision 3: SDK helpers create typed envelopes, not direct transport - -The SDK will provide types, permission helpers, request builders, and runtime guards. It will not own network transport or platform authentication. `platform_web/` host code can later use the same envelopes to communicate with embedded plugin pages. - -Alternative considered: ship a full SDK client that calls platform APIs directly from plugin page code. Rejected because plugin pages must remain behind the platform host bridge and must not receive raw auth storage. - -### Decision 4: Duplicate root-owned contract shapes deliberately - -`plugins/`, `platform_web/`, and `platform/` will each own local contract definitions that mirror the bridge surface they need. Tests and OpenSpec requirements keep the shapes aligned until a future generated contract package exists. - -Alternative considered: import TypeScript SDK types directly into the frontend or backend. Rejected because the repository rules require explicit contract packages or copied/generated contracts instead of casual cross-root imports. - -### Decision 5: AI bridge requests carry purposes, not provider configuration - -`ai.invoke` bridge requests will include an AI purpose and bounded input metadata. Platform validation confirms the purpose is allowed by the plugin manifest. Provider base URLs, API keys, model routing, and raw responses remain controlled by platform services. - -Alternative considered: let plugin pages choose provider IDs or submit provider credentials. Rejected because AI provider keys and routing policy belong to `platform/`. - -## Risks / Trade-offs - -- [Risk] Mirrored TypeScript and Go action constants can drift. Mitigation: add focused tests and keep the action/permission matrix small until generated contracts are introduced. -- [Risk] Early bridge action names may be too coarse for later workflows. Mitigation: keep request payloads metadata-only where possible and add new actions through explicit OpenSpec changes. -- [Risk] Browser host work may need iframe lifecycle decisions later. Mitigation: this change establishes only host-side contracts/utilities, leaving page loading to the console-shell change. -- [Risk] Permission checks can become duplicated between platform and host utilities. Mitigation: platform remains authoritative; host checks are UX preflight only. - -## Migration Plan - -1. Add the bridge contract and permission matrix in OpenSpec. -2. Extend plugin manifest schema/types and the development example with bridge page requirements. -3. Add SDK helpers and tests for typed bridge envelopes and local permission checks. -4. Add platform bridge DTO/domain/validator/service/API route with tests for authorization decisions. -5. Add platform_web bridge host types/utilities and tests for safe context construction. -6. Validate with plugin tests, frontend tests, platform tests, `scripts/check-structure.sh`, and strict OpenSpec validation. - -Rollback is straightforward before downstream UI depends on it: remove the bridge API route, SDK helpers, frontend host utilities, manifest bridge fields, and this change's OpenSpec artifacts. - -## Open Questions - -- Whether future plugin page loading should use iframe `postMessage`, module federation, static asset hosting, or another sandbox strategy. -- Whether a later generated contract package should replace copied bridge action constants across roots. diff --git a/openspec/changes/implement-plugin-bridge-and-sdk/proposal.md b/openspec/changes/implement-plugin-bridge-and-sdk/proposal.md deleted file mode 100644 index fd4fe1e..0000000 --- a/openspec/changes/implement-plugin-bridge-and-sdk/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -Validated plugin manifests can now register game management plugins, but plugin pages still lack a safe runtime bridge and authors lack a typed SDK for calling platform-mediated abilities. This change establishes the browser-side plugin boundary needed before the plugin marketplace and server workflows can host real plugin UI. - -## What Changes - -- Add a plugin page bridge contract that exposes only scoped platform abilities to plugin UI code. -- Add a TypeScript plugin SDK with bridge message types, permission checks, request/response helpers, and safe error handling. -- Add platform API support for validating plugin bridge sessions and evaluating requested actions against manifest permissions. -- Update the example development plugin to declare bridge requirements and exercise the SDK without direct run, host path, socket, credential, or raw AI key access. -- Add focused tests for bridge permission decisions, SDK type/runtime behavior, and platform validators. - -## Capabilities - -### New Capabilities - -- `plugin-bridge-and-sdk`: Defines the platform-mediated plugin page bridge, SDK contract, permission enforcement, and safe capability surface for game management plugin pages. - -### Modified Capabilities - -- None. - -## Impact - -- `plugins/`: SDK source, bridge contracts, manifest/schema additions, example plugin declarations, and TypeScript tests. -- `platform/`: bridge session/action DTOs, domain types, validators, service logic, API route, and Go tests. -- `platform_web/`: bridge host contracts/utilities and tests that can later be used by plugin marketplace pages. -- OpenSpec artifacts and validation for the new `plugin-bridge-and-sdk` capability. diff --git a/openspec/changes/implement-plugin-bridge-and-sdk/specs/plugin-bridge-and-sdk/spec.md b/openspec/changes/implement-plugin-bridge-and-sdk/specs/plugin-bridge-and-sdk/spec.md deleted file mode 100644 index 91cccc5..0000000 --- a/openspec/changes/implement-plugin-bridge-and-sdk/specs/plugin-bridge-and-sdk/spec.md +++ /dev/null @@ -1,76 +0,0 @@ -## ADDED Requirements - -### Requirement: Plugin bridge exposes only platform-mediated actions - -The system SHALL define a plugin page bridge with narrow action names for server context reads, job dispatch, log queries, artifact references, scoped file requests, and platform-mediated AI invocation. - -#### Scenario: Plugin page requests allowed bridge action - -- **WHEN** a plugin page requests a bridge action declared by the bridge contract and permitted by its manifest metadata -- **THEN** the platform authorizes the request without exposing run credentials, raw host paths, direct sockets, storage backend credentials, platform auth storage, or AI provider keys - -#### Scenario: Plugin page requests unsupported bridge action - -- **WHEN** a plugin page requests an action outside the bridge contract -- **THEN** the platform rejects the request with a validation error before dispatching any run, file, log, artifact, or AI work - -### Requirement: Bridge permissions are enforced from manifest metadata - -The system SHALL evaluate each bridge action against the installed plugin's declared permissions, page permissions, and AI purposes before allowing the action. - -#### Scenario: Missing permission denies bridge action - -- **WHEN** a plugin page requests `files.request` without the required file permission in its manifest or page permissions -- **THEN** the platform returns a denied bridge authorization decision and does not create a file job - -#### Scenario: Allowed AI purpose authorizes AI request - -- **WHEN** a plugin page requests `ai.invoke` with an AI purpose declared by the plugin manifest and the plugin has `ai.invoke` permission -- **THEN** the platform returns an allowed bridge authorization decision without exposing provider base URLs or API keys - -#### Scenario: Undeclared AI purpose denies AI request - -- **WHEN** a plugin page requests `ai.invoke` with a purpose not declared by the plugin manifest -- **THEN** the platform rejects or denies the request before invoking any AI provider - -### Requirement: Plugin SDK provides typed bridge helpers - -The plugin SDK SHALL provide TypeScript types and helpers for bridge context, bridge action names, bridge request envelopes, bridge responses, permission checks, and safe errors. - -#### Scenario: SDK builds typed bridge request - -- **WHEN** plugin author code builds a request for a supported bridge action through SDK helpers -- **THEN** the request envelope includes plugin ID, route key, action, request ID, and scoped payload fields that can be validated by the platform host - -#### Scenario: SDK detects missing local permission - -- **WHEN** plugin author code checks a bridge context for a missing permission -- **THEN** the SDK helper returns a negative result without performing transport or privileged work - -### Requirement: Browser host creates safe bridge context - -The frontend host SHALL construct plugin bridge context from installed plugin metadata, current route, selected server instance, safe theme tokens, and effective permissions only. - -#### Scenario: Host context omits secrets - -- **WHEN** the browser host creates bridge context for a plugin page -- **THEN** the context excludes raw AI keys, platform auth storage, run credentials, direct sockets, raw host paths, and storage backend credentials - -#### Scenario: Host filters page permissions - -- **WHEN** a plugin page declares route-specific permissions -- **THEN** the host context contains only permissions allowed by both the plugin manifest and the current page declaration - -### Requirement: Bridge implementation respects root ownership boundaries - -The system SHALL keep plugin SDK, frontend host utilities, and platform authorization logic in their owning roots without casual cross-root imports. - -#### Scenario: Contracts are copied through explicit root files - -- **WHEN** bridge action or permission contracts are needed in multiple roots -- **THEN** each root owns an explicit local contract file or generated/copy artifact instead of importing implementation code from another root - -#### Scenario: Structure validation passes after bridge implementation - -- **WHEN** the bridge and SDK implementation is complete -- **THEN** repository structure validation passes without placing implementation code outside `plugins/`, `platform_web/`, or `platform/` diff --git a/openspec/changes/implement-plugin-bridge-and-sdk/tasks.md b/openspec/changes/implement-plugin-bridge-and-sdk/tasks.md deleted file mode 100644 index e5acacd..0000000 --- a/openspec/changes/implement-plugin-bridge-and-sdk/tasks.md +++ /dev/null @@ -1,38 +0,0 @@ -## 1. Plugin SDK and Manifest Contract - -- [x] 1.1 Extend plugin manifest schema, SDK types, and the development example with bridge action/page declarations. -- [x] 1.2 Add SDK bridge action, request envelope, response, safe error, and permission helper types. -- [x] 1.3 Add plugin SDK tests for request builders, local permission checks, and forbidden transport assumptions. - -## 2. Platform Bridge Authorization - -- [x] 2.1 Add platform domain and DTO types for plugin bridge sessions, actions, requests, and authorization decisions. -- [x] 2.2 Add platform validators that map bridge actions to required permissions and AI purposes. -- [x] 2.3 Add platform service logic and API route for bridge action authorization. -- [x] 2.4 Add Go tests for allowed actions, missing permissions, unsupported actions, and undeclared AI purposes. - -## 3. Frontend Host Bridge Utilities - -- [x] 3.1 Add frontend bridge host contract/types for safe plugin page context. -- [x] 3.2 Add frontend utilities that filter page permissions against manifest permissions and exclude secret-bearing fields. -- [x] 3.3 Add frontend tests for safe context creation and page permission filtering. - -## 4. Verification - -- [x] 4.1 Run plugin SDK/schema tests. -- [x] 4.2 Run platform bridge authorization tests. -- [x] 4.3 Run platform_web bridge utility tests. -- [x] 4.4 Run `scripts/check-structure.sh`. -- [x] 4.5 Run `openspec validate implement-plugin-bridge-and-sdk --strict`. -- [x] 4.6 Record verification evidence in this task file and only then mark verification tasks complete. - -## Evidence - -- `cd platform && go test ./...`: passed. -- `cd plugins && npm test`: passed, 1 file / 7 tests. -- `cd plugins && npm run typecheck`: passed. -- `cd plugins && npm run validate:manifest`: passed for `examples/dev-game-plugin/manifest.json`. -- `cd platform_web && npm test`: passed, 4 files / 7 tests. -- `cd platform_web && npm run typecheck`: passed. -- `scripts/check-structure.sh`: passed. -- `openspec validate implement-plugin-bridge-and-sdk --strict`: passed. diff --git a/openspec/changes/implement-plugin-marketplace-api-driven-ui/.openspec.yaml b/openspec/changes/implement-plugin-marketplace-api-driven-ui/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-plugin-marketplace-api-driven-ui/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-plugin-marketplace-api-driven-ui/design.md b/openspec/changes/implement-plugin-marketplace-api-driven-ui/design.md deleted file mode 100644 index 39d0bfb..0000000 --- a/openspec/changes/implement-plugin-marketplace-api-driven-ui/design.md +++ /dev/null @@ -1,60 +0,0 @@ -## Context - -The platform can validate and register game plugin manifests, and the frontend already has the first-party Plugin Marketplace area. The remaining gap is that the page is not yet an authoritative view over platform registry data. This change makes the marketplace page API-driven while keeping commerce concepts out of scope. - -## Goals / Non-Goals - -**Goals:** - -- Expose marketplace list and detail data from platform registry metadata. -- Let operators filter installed game management plugins by status, server type, keyword, and capability. -- Allow safe install/enable/disable state changes without exposing implementation internals. -- Centralize frontend API types and client methods outside page-local hidden types. -- Add tests and browser walkthrough evidence for loading, errors, filtering, install/state actions, and no-secret rendering. - -**Non-Goals:** - -- No billing, pricing, cloud host sales, provider marketplace, ratings, reviews, checkout, or subscription behavior. -- No plugin page execution, bridge transport, run-side action execution, or AI invocation implementation. -- No raw host paths, direct run sockets, raw credentials, or raw AI provider keys in responses. -- No new visual system that replaces the platform_web theme. - -## Decisions - -### Decision 1: Marketplace data is a view over installed registry metadata - -Marketplace APIs will project installed plugin metadata into list/detail DTOs. The response includes identity, version, server type, status, description, pages, capabilities, permissions, and AI purposes, but not commerce data or backend internals. - -Alternative considered: create a separate catalog model with publish/store metadata. Rejected because this repository is a game server management platform, and current needs are covered by installed registry data. - -### Decision 2: Install and state actions remain metadata-only - -Install/enable/disable actions update platform plugin registry state and return the updated marketplace DTO. They do not download external packages or execute run jobs in this change. - -Alternative considered: trigger package download and runtime deployment from the marketplace page. Rejected because package acquisition and run execution require separate explicit changes. - -### Decision 3: Frontend fallback is isolated to development - -The page may keep a clearly isolated local fallback for standalone frontend development, but production flow prefers API data and shows API errors. Tests assert fallback does not leak into successful API flows. - -Alternative considered: remove all fallback state immediately. Rejected because local frontend demos still need useful data when the backend is absent. - -## Risks / Trade-offs - -- [Risk] Marketplace APIs duplicate some game plugin list/detail behavior. Mitigation: implement them as service projections over the same registry metadata. -- [Risk] Install state can be confused with package acquisition. Mitigation: name docs and DTO fields around installed/active registry state only. -- [Risk] Frontend state can drift from backend after actions. Mitigation: action methods return updated DTOs and tests cover refresh/update behavior. - -## Migration Plan - -1. Add platform marketplace DTOs, validators, service projection, routes, and docs. -2. Update frontend API contracts and marketplace page to consume API data. -3. Add backend and frontend tests. -4. Run browser walkthrough, structure check, and strict OpenSpec validation. - -Rollback before dependent changes is removal of marketplace projection routes/page API integration and this change's artifacts. - -## Open Questions - -- Whether future plugin package publication should use signed artifacts or an internal admin upload flow. -- Whether marketplace sorting should later incorporate operational health or compatibility scores. diff --git a/openspec/changes/implement-plugin-marketplace-api-driven-ui/proposal.md b/openspec/changes/implement-plugin-marketplace-api-driven-ui/proposal.md deleted file mode 100644 index 27f6bc8..0000000 --- a/openspec/changes/implement-plugin-marketplace-api-driven-ui/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -The plugin registry and bridge contracts exist, but the plugin marketplace page still needs to be driven by platform API data instead of hardcoded catalog state. Operators need to browse installed game management plugins, inspect manifest-backed capabilities, and start install/enable workflows without exposing host paths, run sockets, credentials, or raw AI provider keys. - -## What Changes - -- Add marketplace-focused API responses derived from installed game plugin registry metadata. -- Add platform service and handler behavior for listing marketplace plugins, viewing detail, and changing install/enable state through safe metadata workflows. -- Replace production hardcoded marketplace data in `platform_web` with API client calls, typed contracts, loading/error states, filters, and plugin detail/install actions. -- Preserve the magical-girl crystal-moonlight console visual direction while keeping the marketplace operational and game-management focused. -- Add backend, frontend, browser walkthrough, structure, and strict OpenSpec verification. - -## Capabilities - -### New Capabilities - -- `plugin-marketplace-api-driven-ui`: Platform and frontend workflows for rendering the plugin marketplace from registry APIs and managing installed plugin state safely. - -### Modified Capabilities - -- Builds on `plugin-registry-and-manifest-validation` and `plugin-bridge-and-sdk`; it does not change their archived contracts directly. - -## Impact - -- Affects `platform/` game plugin DTOs, service, validators, API routes, and docs. -- Affects `platform_web/` API types/client methods, plugin marketplace page/components, and tests. -- Does not add billing, cloud host sales, provider marketplaces, unrelated SaaS marketplace behavior, direct plugin-run access, or raw key exposure. diff --git a/openspec/changes/implement-plugin-marketplace-api-driven-ui/specs/plugin-marketplace-api-driven-ui/spec.md b/openspec/changes/implement-plugin-marketplace-api-driven-ui/specs/plugin-marketplace-api-driven-ui/spec.md deleted file mode 100644 index 3fa151d..0000000 --- a/openspec/changes/implement-plugin-marketplace-api-driven-ui/specs/plugin-marketplace-api-driven-ui/spec.md +++ /dev/null @@ -1,57 +0,0 @@ -## ADDED Requirements - -### Requirement: Marketplace APIs expose registry-backed plugin catalog data - -The platform SHALL expose marketplace list and detail APIs derived from installed game plugin registry metadata. - -#### Scenario: Marketplace list returns installed plugin data -- **WHEN** a client requests marketplace plugins with optional status, server type, capability, or keyword filters -- **THEN** the platform MUST return bounded plugin summaries with identity, version, display metadata, server type, install status, capabilities, pages, permissions, and AI purposes - -#### Scenario: Marketplace detail returns safe plugin metadata -- **WHEN** a client requests one marketplace plugin detail -- **THEN** the platform MUST return manifest-backed metadata and MUST NOT expose raw host paths, direct run sockets, raw credentials, platform auth storage, storage backend credentials, or raw AI provider keys - -### Requirement: Marketplace state actions are platform-mediated - -The platform SHALL provide safe marketplace actions for changing installed plugin state without external package download or run execution. - -#### Scenario: Plugin is enabled or disabled -- **WHEN** an operator enables or disables a marketplace plugin -- **THEN** the platform MUST validate the plugin ID, update registry state, and return the redacted marketplace plugin detail - -#### Scenario: Unknown plugin action is rejected -- **WHEN** an operator submits a state action for a missing plugin ID or unsupported action -- **THEN** the platform MUST return a stable JSON error and MUST NOT change other plugin state - -### Requirement: Marketplace frontend uses centralized API contracts - -The frontend SHALL keep marketplace API types and client methods in `platform_web/api` and SHALL use them from the plugin marketplace page. - -#### Scenario: Page loads marketplace data -- **WHEN** the Plugin Marketplace page renders with a reachable backend -- **THEN** it MUST fetch plugin summaries from the marketplace API and render loading, error, empty, and populated states - -#### Scenario: Page filters and opens detail -- **WHEN** an operator filters or selects a plugin -- **THEN** the page MUST use API-backed data to update the list/detail view without relying on hidden page-local DTO contracts - -### Requirement: Marketplace UI preserves safety and theme constraints - -The Plugin Marketplace page SHALL preserve the platform_web magical-girl crystal-moonlight operations console style and avoid unsafe or unrelated marketplace concepts. - -#### Scenario: UI renders plugin cards and actions -- **WHEN** marketplace data is displayed -- **THEN** the UI MUST show game plugin metadata, capability tags, status controls, and install/enable actions without billing, cloud host sales, provider marketplace, raw secrets, or generic SaaS storefront behavior - -#### Scenario: Browser walkthrough verifies no secret rendering -- **WHEN** frontend marketplace behavior is claimed complete -- **THEN** a browser walkthrough MUST verify the page renders API-backed plugin data and visible text excludes raw credential markers - -### Requirement: Marketplace implementation is verified - -The change SHALL include backend tests, frontend tests/build, browser walkthrough evidence, structure validation, and strict OpenSpec validation. - -#### Scenario: Verification commands pass -- **WHEN** the change is complete -- **THEN** platform tests, platform_web tests/typecheck/build, `scripts/check-structure.sh`, and `openspec validate implement-plugin-marketplace-api-driven-ui --strict` MUST pass diff --git a/openspec/changes/implement-plugin-marketplace-api-driven-ui/tasks.md b/openspec/changes/implement-plugin-marketplace-api-driven-ui/tasks.md deleted file mode 100644 index 24636fe..0000000 --- a/openspec/changes/implement-plugin-marketplace-api-driven-ui/tasks.md +++ /dev/null @@ -1,42 +0,0 @@ -## 1. Platform Marketplace Contracts - -- [x] 1.1 Add marketplace plugin summary/detail DTOs and domain projection contracts from registered plugin metadata. -- [x] 1.2 Add validators for marketplace filters, plugin IDs, bounded list responses, supported state actions, and response safety. -- [x] 1.3 Add service methods for marketplace list, detail, install-state projection, and enable/disable actions. - -## 2. Platform Marketplace API - -- [x] 2.1 Implement marketplace list and detail routes under the platform API surface. -- [x] 2.2 Implement safe install/enable/disable state action routes without package download or run execution. -- [x] 2.3 Update platform route/protocol documentation for marketplace APIs and deferred package/runtime behavior. -- [x] 2.4 Add platform service/API tests for filters, detail, state actions, missing plugins, unsupported actions, and no-secret responses. - -## 3. Frontend Marketplace API Integration - -- [x] 3.1 Add centralized `platform_web/api` marketplace types and `PlatformApiClient` methods. -- [x] 3.2 Update Plugin Marketplace page to load API data, support filters/search/detail, and render loading/error/empty/populated states. -- [x] 3.3 Wire install/enable/disable controls to API actions and update page state from API responses. -- [x] 3.4 Isolate any local fallback data to standalone development and keep production API flow authoritative. -- [x] 3.5 Add frontend tests for loading, errors, filters, detail selection, state actions, and no raw key/path rendering. - -## 4. Verification - -- [x] 4.1 Run `cd platform && go test ./...` and record evidence. -- [x] 4.2 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 4.3 Run browser walkthrough for the Plugin Marketplace API-driven page and record evidence. -- [x] 4.4 Run `scripts/check-structure.sh` and record evidence. -- [x] 4.5 Run `openspec validate implement-plugin-marketplace-api-driven-ui --strict` and record evidence. - -## Evidence - -- 2026-07-06: `cd platform && go test ./domain ./dto ./validator ./service ./api` passed after adding marketplace contracts, validators, service methods, and routes. -- 2026-07-06: `cd platform && go test ./service ./api -run 'TestCoreServiceMarketplacePluginsAreFilteredSafeAndStateful|TestPluginMarketplaceAPIListsDetailsAndChangesStateSafely|TestGamePluginManifestRegistryAPI'` passed, covering filters, detail, install/enable/disable state actions, missing plugins, unsupported actions, unsafe filters, and no-secret API response assertions. -- 2026-07-06: `cd platform_web && npm run typecheck` passed after adding marketplace API types/client methods and the API-driven Plugins page. -- 2026-07-06: `cd platform_web && npm test -- --run api/client.test.ts pages/PluginsPage.test.tsx pages/ConsolePages.test.tsx` passed, covering marketplace client URLs/actions, loading/error/API-backed detail rendering, standalone fallback labeling, state controls, and no raw key/path fragments. -- 2026-07-06: `cd platform && go test ./...` passed. -- 2026-07-06: `cd platform_web && npm test` passed with 10 files / 36 tests. -- 2026-07-06: `cd platform_web && npm run typecheck` passed. -- 2026-07-06: `cd platform_web && npm run build` passed. -- 2026-07-06: Browser walkthrough against `http://127.0.0.1:5176/#/plugins` and platform API `127.0.0.1:18089` rendered API-backed `Example Server`, `logs.query`, `平台 API`, successfully applied the disable state action, and verified `/Users/`, `unix://`, `Bearer `, `sk-`, `password=`, `apiKeyRef`, `rawApiKey`, and `billing` were absent from visible text. -- 2026-07-06: `scripts/check-structure.sh` passed. -- 2026-07-06: `openspec validate implement-plugin-marketplace-api-driven-ui --strict` passed; PostHog telemetry flush logged a restricted-network DNS error after local validation succeeded. diff --git a/openspec/changes/implement-plugin-page-bridge-execution/.openspec.yaml b/openspec/changes/implement-plugin-page-bridge-execution/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-plugin-page-bridge-execution/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-plugin-page-bridge-execution/design.md b/openspec/changes/implement-plugin-page-bridge-execution/design.md deleted file mode 100644 index 8510bd0..0000000 --- a/openspec/changes/implement-plugin-page-bridge-execution/design.md +++ /dev/null @@ -1,60 +0,0 @@ -## Context - -`implement-plugin-bridge-and-sdk` defines typed bridge envelopes and authorization decisions, but execution is still theoretical. This change makes plugin pages useful in the console by adding the host-side transport and backend execution adapter for allowed bridge actions. The platform remains authoritative: browser host checks improve UX, but backend validation decides whether a request can execute. - -## Goals / Non-Goals - -**Goals:** - -- Create safe plugin bridge sessions in `platform_web` from registry/page/server context. -- Dispatch plugin page bridge requests through centralized frontend API client methods. -- Add backend execution behavior for supported bridge actions by calling existing platform services instead of proxying arbitrary API paths. -- Return typed success/error envelopes to plugin pages. -- Add tests and browser walkthrough evidence for allowed actions, denied actions, and no secret exposure. - -**Non-Goals:** - -- No arbitrary HTTP proxy from plugin pages to platform APIs. -- No direct run sockets, host paths, raw credentials, auth storage, artifact storage credentials, or raw AI provider keys in plugin page context or responses. -- No iframe sandbox policy overhaul beyond what is necessary for bridge host execution. -- No package download, plugin marketplace commerce, billing, cloud host sales, or provider marketplace behavior. - -## Decisions - -### Decision 1: Backend execution uses an action switch over safe services - -Bridge execution maps each supported action to a named backend service method, such as server context reads, job dispatch, log queries, artifact open/download metadata, scoped file dispatch, or platform-mediated AI invocation. Unsupported actions fail before side effects. - -Alternative considered: accept a plugin-supplied URL/path and proxy it. Rejected because arbitrary proxying bypasses reviewable permission mapping. - -### Decision 2: Host context is short-lived and derived - -The frontend host builds session context from selected server instance, plugin page metadata, safe theme tokens, and effective permissions. It does not persist platform auth tokens or raw backend state in plugin page-visible structures. - -Alternative considered: pass the normal platform API client or auth storage into plugin pages. Rejected because plugin code is not a credential boundary. - -### Decision 3: Execution envelopes are typed and auditable - -Every bridge execution request carries request ID, plugin ID, route key, action, server instance scope, purpose metadata, and bounded payload. Backend responses include allowed/denied state, typed result, and safe error details. - -Alternative considered: reuse authorization-only DTOs for execution results. Rejected because execution needs result references and error details distinct from preflight authorization. - -## Risks / Trade-offs - -- [Risk] Supported bridge action behavior depends on other platform capabilities. Mitigation: actions whose downstream service is not available return explicit unsupported/deferred errors and tests cover the available set. -- [Risk] Browser host checks can be bypassed. Mitigation: backend validation repeats all permission and safety checks. -- [Risk] Plugin page UI can create noisy request loops. Mitigation: host utilities include request bounds and cancellation/error state tests. - -## Migration Plan - -1. Add bridge execution DTOs/domain/validators/services/routes in platform. -2. Add frontend host transport/API client/session utilities and tests. -3. Update plugin SDK/example tests to use execution envelopes. -4. Update docs and run full verification including browser walkthrough. - -Rollback removes bridge execution routes, host transport utilities, SDK example changes, and this change's artifacts before downstream plugin pages depend on it. - -## Open Questions - -- Which sandbox mechanism should eventually host third-party plugin page assets in production? -- Whether bridge execution audit events should be a separate observability change. diff --git a/openspec/changes/implement-plugin-page-bridge-execution/proposal.md b/openspec/changes/implement-plugin-page-bridge-execution/proposal.md deleted file mode 100644 index e4a4f54..0000000 --- a/openspec/changes/implement-plugin-page-bridge-execution/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -The plugin SDK and authorization contract exist, but plugin pages still cannot execute real platform-mediated bridge actions from the management console. Operators need plugin UI pages to request allowed server, job, log, artifact, file, and AI capabilities through a host bridge that keeps platform auth, run sockets, host paths, and provider credentials hidden. - -## What Changes - -- Add frontend plugin page host execution utilities for creating safe bridge sessions and dispatching bridge action requests through platform APIs. -- Add platform bridge execution routes/services that authorize each request and fan out only to existing safe platform capabilities. -- Add plugin SDK/example coverage for request envelopes and host-mediated response/error handling. -- Update plugin page documentation and tests to prove unsupported actions, missing permissions, raw paths, sockets, credentials, and raw AI keys are rejected. -- Add browser walkthrough for an embedded plugin page workflow. - -## Capabilities - -### New Capabilities - -- `plugin-page-bridge-execution`: Executes plugin page bridge requests through the platform host and backend authorization layer without exposing unsafe internals. - -### Modified Capabilities - -- Builds on `plugin-bridge-and-sdk`, `config-write-and-file-dispatch`, `platform-mediated-ai-invocation`, and artifact/log/job capabilities as they become available. - -## Impact - -- Affects `platform/`, `platform_web/`, and `plugins/`. -- Adds backend DTO/service/API behavior for bridge execution, frontend bridge host transport, SDK/example tests, and documentation. -- Does not add direct plugin-to-run access, direct platform auth sharing, raw credentials, host path exposure, billing, cloud host sales, or unrelated marketplace features. diff --git a/openspec/changes/implement-plugin-page-bridge-execution/specs/plugin-page-bridge-execution/spec.md b/openspec/changes/implement-plugin-page-bridge-execution/specs/plugin-page-bridge-execution/spec.md deleted file mode 100644 index 914c609..0000000 --- a/openspec/changes/implement-plugin-page-bridge-execution/specs/plugin-page-bridge-execution/spec.md +++ /dev/null @@ -1,61 +0,0 @@ -## ADDED Requirements - -### Requirement: Plugin page host creates safe executable bridge sessions - -The frontend SHALL create plugin page bridge sessions from installed plugin metadata, selected route, selected server instance, safe theme tokens, and effective permissions only. - -#### Scenario: Host session omits secrets -- **WHEN** a plugin page bridge session is created -- **THEN** the session context MUST omit raw platform auth storage, raw AI keys, provider base URL secrets, run credentials, direct sockets, raw host paths, and storage backend credentials - -#### Scenario: Host session filters permissions -- **WHEN** a plugin page declares route-specific permissions -- **THEN** the host MUST include only permissions allowed by both plugin manifest metadata and the page declaration - -### Requirement: Plugin page bridge requests execute through platform APIs - -The frontend SHALL dispatch plugin page bridge action requests through centralized platform API client methods rather than direct plugin fetches to arbitrary backend paths. - -#### Scenario: Allowed request is dispatched -- **WHEN** a plugin page sends a supported action with required permissions and bounded payload -- **THEN** the host MUST submit a typed bridge execution request to the platform and return a typed bridge response to the plugin page - -#### Scenario: Unsupported request is rejected locally or by platform -- **WHEN** a plugin page sends an unsupported action or unsafe payload -- **THEN** the host or platform MUST return a safe error envelope and MUST NOT dispatch run, file, artifact, log, job, or AI work - -### Requirement: Platform authorizes and executes supported bridge actions - -The platform SHALL authorize every bridge execution request against plugin metadata and execute only supported platform-mediated actions. - -#### Scenario: Missing permission prevents execution -- **WHEN** a plugin page requests an action without the required manifest/page permission -- **THEN** the platform MUST deny the request before side effects occur - -#### Scenario: Allowed job dispatch request creates platform job -- **WHEN** a plugin page requests an allowed job dispatch action with a valid server scope -- **THEN** the platform MUST create or return a platform-mediated job reference without exposing run sockets, credentials, or host paths - -#### Scenario: Allowed file request uses scoped dispatch -- **WHEN** a plugin page requests an allowed file action -- **THEN** the platform MUST use scoped file/config dispatch semantics and MUST NOT accept raw absolute host paths - -### Requirement: Bridge execution responses are safe and typed - -The system SHALL return bridge execution responses as typed success or error envelopes with redacted result references. - -#### Scenario: Execution succeeds -- **WHEN** a supported bridge action completes or queues work -- **THEN** the response MUST include request ID, action, status, and scoped result references without raw secrets or direct storage/run internals - -#### Scenario: Execution fails -- **WHEN** validation, authorization, downstream service, or cancellation fails -- **THEN** the response MUST include a safe error code/message and MUST NOT include raw credentials, host paths, sockets, or provider keys - -### Requirement: Plugin page bridge execution is verified end to end - -The change SHALL include backend tests, frontend tests/build, plugin SDK/example tests, browser walkthrough evidence, structure validation, and strict OpenSpec validation. - -#### Scenario: Verification commands pass -- **WHEN** the change is complete -- **THEN** platform tests, platform_web tests/typecheck/build, plugin tests/typecheck, `scripts/check-structure.sh`, and `openspec validate implement-plugin-page-bridge-execution --strict` MUST pass diff --git a/openspec/changes/implement-plugin-page-bridge-execution/tasks.md b/openspec/changes/implement-plugin-page-bridge-execution/tasks.md deleted file mode 100644 index 0864b49..0000000 --- a/openspec/changes/implement-plugin-page-bridge-execution/tasks.md +++ /dev/null @@ -1,46 +0,0 @@ -## 1. Platform Bridge Execution Contracts - -- [x] 1.1 Add platform domain and DTO contracts for bridge execution requests, responses, result refs, and safe errors. -- [x] 1.2 Add validators for action support, required permissions, page route scope, server scope, payload bounds, AI purposes, and unsafe path/secret/socket content. -- [x] 1.3 Add service methods that authorize and execute supported bridge actions through existing platform services. - -## 2. Platform Bridge Execution API - -- [x] 2.1 Implement bridge execution route using named DTOs and service methods. -- [x] 2.2 Map supported actions to safe service calls for server context, job dispatch, logs, artifacts, scoped files, and platform-mediated AI where available. -- [x] 2.3 Update platform route/protocol documentation for plugin bridge execution and deferred unsupported actions. -- [x] 2.4 Add platform tests for allowed execution, denied permissions, unsupported actions, unsafe payloads, safe errors, and no-secret responses. - -## 3. Frontend Host Execution - -- [x] 3.1 Add centralized frontend API types/client methods for bridge execution. -- [x] 3.2 Add plugin page host session and request dispatcher utilities that construct safe context and return typed envelopes. -- [x] 3.3 Update plugin page host UI flow to use bridge execution utilities for embedded plugin actions. -- [x] 3.4 Add frontend tests for session safety, permission filtering, allowed dispatch, denied dispatch, cancellation/error states, and no raw secret rendering. - -## 4. Plugin SDK And Example - -- [x] 4.1 Extend plugin SDK helpers and example plugin page code to exercise bridge execution envelopes. -- [x] 4.2 Add plugin tests for execution request builders, safe error parsing, and forbidden direct transport assumptions. - -## 5. Verification - -- [x] 5.1 Run `cd platform && go test ./...` and record evidence. -- [x] 5.2 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 5.3 Run `cd plugins && npm run typecheck && npm test` and record evidence. -- [x] 5.4 Run browser walkthrough for plugin page bridge execution and record evidence. -- [x] 5.5 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.6 Run `openspec validate implement-plugin-page-bridge-execution --strict` and record evidence. - -## Evidence - -- 2026-07-06: `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -run TestPluginBridgeExecuteAPI -count=1` passed for bridge execution route, service mappings, safe errors, unsafe payload rejection, and no-secret response checks. -- 2026-07-06: `cd platform_web && npm run typecheck` passed after adding bridge execution API types/client, host dispatcher, and server detail execution panel. -- 2026-07-06: `cd platform_web && npm test -- --run utils/pluginBridgeHost.test.ts api/client.test.ts` passed, covering safe host context, permission filtering, allowed dispatch, unsafe/denied/cancelled states, and API client routing. -- 2026-07-06: `cd plugins && npm run typecheck` and `cd plugins && npm test -- --run tests/manifest-validation.test.ts` passed for SDK execution envelope helpers and no direct transport assumptions. -- 2026-07-06: `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./...` passed. -- 2026-07-06: `cd platform_web && npm run typecheck`, `cd platform_web && npm test`, and `cd platform_web && npm run build` passed. -- 2026-07-06: `cd plugins && npm run typecheck` and `cd plugins && npm test` passed. -- 2026-07-06: Browser walkthrough passed using a local mock platform API plus headless Chrome: logged in, opened `#/servers/server-bridge-walkthrough`, switched to `插件控制`, clicked `读取上下文`, and verified `服务器上下文 server-bridge-walkthrough 已返回` with no forbidden fragments rendered. -- 2026-07-06: `scripts/check-structure.sh` passed. -- 2026-07-06: `openspec validate implement-plugin-page-bridge-execution --strict` passed (`Change 'implement-plugin-page-bridge-execution' is valid`; PostHog DNS flush warnings were non-fatal telemetry failures). diff --git a/openspec/changes/implement-plugin-registry-and-manifest-validation/.openspec.yaml b/openspec/changes/implement-plugin-registry-and-manifest-validation/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-plugin-registry-and-manifest-validation/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-plugin-registry-and-manifest-validation/design.md b/openspec/changes/implement-plugin-registry-and-manifest-validation/design.md deleted file mode 100644 index b232258..0000000 --- a/openspec/changes/implement-plugin-registry-and-manifest-validation/design.md +++ /dev/null @@ -1,68 +0,0 @@ -## Context - -The platform already has a basic `GamePlugin` resource in `platform/` and an initial JSON Schema validator in `plugins/`. Those pieces are not yet enough for the plugin marketplace and server creation backlog because registration can still be assembled by hand instead of coming from a validated game management plugin manifest. - -This change covers the first registry boundary between `plugins/` and `platform/`: plugin authors validate manifest files in the plugin workspace, while the platform accepts a structured manifest registration payload, validates the same safety constraints in backend validators, stores only registry metadata, and exposes that metadata through the existing game plugin APIs. - -## Goals / Non-Goals - -**Goals:** - -- Define one concrete manifest contract for game management plugins. -- Reject unsafe manifest requests for raw run, host path, credential, socket, and raw AI key access. -- Register installed plugins from manifest metadata through platform DTO/service/API layers. -- Keep registry responses useful for plugin marketplace display without exposing raw paths, raw credentials, run internals, or AI provider keys. -- Add focused schema, validator, service, and API tests. - -**Non-Goals:** - -- Do not implement plugin page bridge runtime, plugin action execution, or SDK transport. -- Do not add billing, cloud host sales, provider marketplaces, or unrelated SaaS marketplace behavior. -- Do not make platform import TypeScript plugin validation code or plugin workspace files directly. -- Do not implement frontend plugin marketplace pages in this change. - -## Decisions - -### Decision 1: Use a copied manifest contract at the root boundary - -`plugins/` owns the JSON Schema and TypeScript validation helper. `platform/` owns named DTOs and domain types that mirror the externally submitted manifest shape. The two roots are kept aligned by tests and OpenSpec requirements rather than direct imports. - -Alternative considered: have platform read `plugins/manifests/game-plugin.manifest.schema.json` directly. Rejected because that would make the backend depend on the plugin workspace file layout and blur root ownership. - -### Decision 2: Platform registration converts manifests to existing registry metadata - -The platform will add a manifest registration service/API path that converts a validated manifest into `GamePlugin` registry metadata. The existing create/list/detail endpoints remain available for low-level metadata tests and future migration, while the new manifest endpoint is the supported installation boundary for plugin manifests. - -Alternative considered: replace `GamePluginCreateRequest` with the manifest shape. Rejected because existing server-management tests and API contracts already use the metadata resource directly. - -### Decision 3: Deny unsafe permissions by explicit allowlists and substring checks - -Plugin manifests may declare scoped permissions such as server lifecycle, file, log, artifact, and platform-mediated AI permissions. They must not declare direct run sockets, raw host paths, raw credentials, raw AI keys, provider keys, or direct run credentials. `plugins/` catches these during schema/test validation and `platform/` repeats the safety validation before registry insertion. - -Alternative considered: rely only on JSON Schema enum restrictions. Rejected because unsafe intent can appear in action paths, capability names, or future fields; backend validation still needs an explicit defense. - -### Decision 4: Registry metadata is marketplace-ready but not commerce-oriented - -Registry responses include identity, version, server type/display name, manifest/schema references, required run capabilities, permissions, pages, and AI purposes. They intentionally exclude pricing, cloud host purchase flows, and provider marketplace concepts. - -Alternative considered: add a richer marketplace catalog model now. Rejected because the repository scope is game server management, and later UI can derive its first catalog view from registry metadata. - -## Risks / Trade-offs - -- [Risk] The plugin manifest contract may evolve when plugin bridge work starts. Mitigation: keep this change focused on registry metadata and add bridge-specific fields in the next OpenSpec change. -- [Risk] Duplicating contract shapes across TypeScript and Go can drift. Mitigation: tests cover the example manifest and platform manifest registration until a generated contract package is introduced. -- [Risk] Strict allowlists can reject useful future plugin capabilities. Mitigation: add new allowed capability/permission keys through explicit OpenSpec changes. - -## Migration Plan - -1. Add plugin schema restrictions, fixtures, and validation tests while keeping the development example valid. -2. Add platform manifest DTO/domain conversion, validators, service registration, and API route tests. -3. Keep existing `POST /api/v1/game-plugins` metadata creation working for current tests. -4. Validate the change with plugin tests, platform tests, structure check, and strict OpenSpec validation. - -Rollback during this phase is straightforward: remove the manifest registration route and schema/test additions before downstream plugin bridge work depends on them. - -## Open Questions - -- Whether manifest contract generation should be added in the plugin bridge change or a later contract-generation change. -- Whether registry metadata should eventually support signed manifest artifacts before plugin publish/install workflows are implemented. diff --git a/openspec/changes/implement-plugin-registry-and-manifest-validation/proposal.md b/openspec/changes/implement-plugin-registry-and-manifest-validation/proposal.md deleted file mode 100644 index 3d4f0f4..0000000 --- a/openspec/changes/implement-plugin-registry-and-manifest-validation/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -Plugin installability is now the next platform dependency: server creation, plugin marketplace display, and plugin bridge work all need a trusted registry of game management plugin manifests. This change adds the manifest validation and registry surface so only safe, well-formed plugins become available to the platform. - -## What Changes - -- Define the first plugin manifest contract for game management plugins, including identity, server type, create form schema, lifecycle actions, run capabilities, UI contribution, and scoped permissions. -- Add manifest validation in `plugins/` with schema fixtures and tests for valid and unsafe manifests. -- Add platform domain, repository, service, DTO, validator, and API handling for registering, listing, and inspecting installed plugin metadata. -- Reject or disable unsafe manifest requests such as raw host paths, direct run sockets, raw credentials, or raw AI provider keys. -- Keep plugin marketplace metadata focused on game server management plugins, not billing, cloud host sales, or unrelated SaaS marketplace features. - -## Capabilities - -### New Capabilities - -- `plugin-registry-and-manifest-validation`: Validates game management plugin manifests and exposes installed plugin registry metadata through platform APIs. - -### Modified Capabilities - -- None. This change builds on the bootstrap plugin and platform-core constraints, which have not yet been archived into `openspec/specs/`. - -## Impact - -- Affects `plugins/` manifest schemas, fixtures, SDK-adjacent types, and validation tests. -- Affects `platform/` plugin domain, DTOs, models, repositories, services, validators, routes, and API tests. -- Adds or updates verification commands for plugin schema tests, platform API tests, `scripts/check-structure.sh`, and strict OpenSpec validation. diff --git a/openspec/changes/implement-plugin-registry-and-manifest-validation/specs/plugin-registry-and-manifest-validation/spec.md b/openspec/changes/implement-plugin-registry-and-manifest-validation/specs/plugin-registry-and-manifest-validation/spec.md deleted file mode 100644 index 0c27d5e..0000000 --- a/openspec/changes/implement-plugin-registry-and-manifest-validation/specs/plugin-registry-and-manifest-validation/spec.md +++ /dev/null @@ -1,52 +0,0 @@ -## ADDED Requirements - -### Requirement: Manifest Schema Validation -The plugin workspace SHALL validate game management plugin manifests with a JSON Schema that defines identity, version, plugin kind, server type, create form schema reference, run capabilities, lifecycle actions, optional pages, AI purposes, and scoped permissions. - -#### Scenario: Development plugin manifest validates -- **WHEN** the development game plugin manifest is validated by the plugin workspace validator -- **THEN** validation MUST pass and its referenced create form schema MUST also validate - -#### Scenario: Unsafe manifest fails validation -- **WHEN** a manifest requests direct run sockets, host paths, raw credentials, raw AI keys, or direct provider key access -- **THEN** validation MUST return violations and MUST NOT treat the plugin as installable - -### Requirement: Platform Manifest Registration -The platform SHALL expose a manifest registration API that accepts a structured game plugin manifest payload and converts it into installed plugin registry metadata. - -#### Scenario: Valid manifest is registered -- **WHEN** a valid game management plugin manifest is submitted to the platform registration API -- **THEN** the platform MUST persist an installed plugin record with manifest reference, create form schema reference, server type, required run capabilities, scoped permissions, pages, and AI purposes - -#### Scenario: Duplicate plugin registration is rejected -- **WHEN** a manifest is submitted for an already registered plugin ID -- **THEN** the platform MUST return a duplicate error and MUST preserve the existing registry record - -### Requirement: Registry Query Surface -The platform SHALL expose plugin registry list and detail responses suitable for the plugin marketplace and server creation workflows. - -#### Scenario: Marketplace lists installed plugins -- **WHEN** platform clients list game plugins by status or server type -- **THEN** each response item MUST include plugin identity, version, server type/display metadata, manifest/schema references, run capabilities, scoped permissions, pages, AI purposes, and install status - -#### Scenario: Registry detail excludes unsafe internals -- **WHEN** platform clients fetch one registered plugin -- **THEN** the response MUST NOT include raw host paths, raw credentials, run connection details, or raw AI provider keys - -### Requirement: Backend Safety Validation -The platform SHALL independently validate plugin manifest safety before registry persistence, regardless of plugin workspace validation results. - -#### Scenario: Raw key request reaches platform -- **WHEN** a registration payload contains raw AI key, provider key, bearer token, or secret-like content -- **THEN** the platform MUST reject the registration with a validation error - -#### Scenario: Direct run or host path request reaches platform -- **WHEN** a registration payload contains direct run socket, direct run credential, or raw host path access requests -- **THEN** the platform MUST reject the registration with a validation error - -### Requirement: Ownership Boundary Preservation -The manifest registry implementation SHALL keep `plugins/` validation code and `platform/` backend code in their own roots and share contract shapes only through named DTO/domain/schema files. - -#### Scenario: Platform validates registration -- **WHEN** platform code handles manifest registration -- **THEN** it MUST use platform DTOs, domain types, and validators rather than importing plugin workspace implementation files diff --git a/openspec/changes/implement-plugin-registry-and-manifest-validation/tasks.md b/openspec/changes/implement-plugin-registry-and-manifest-validation/tasks.md deleted file mode 100644 index 9c07640..0000000 --- a/openspec/changes/implement-plugin-registry-and-manifest-validation/tasks.md +++ /dev/null @@ -1,68 +0,0 @@ -## 1. Plugin Manifest Validation - -- [x] 1.1 Extend the plugin manifest schema and SDK-adjacent types with registry metadata, lifecycle actions, pages, AI purposes, artifacts permission, and explicit safe permission/capability allowlists. -- [x] 1.2 Extend `plugins/scripts/validate-manifest.ts` to reject unsafe raw host path, direct run, raw credential, and raw AI/provider key requests beyond JSON Schema shape validation. -- [x] 1.3 Add plugin validation fixtures/tests for the valid development manifest, invalid create form schema, and unsafe manifest requests. - -## 2. Platform Registry Contracts - -- [x] 2.1 Add platform domain, DTO, model, copy, and conversion contracts for game plugin manifest registration metadata including server display metadata, pages, AI purposes, and validation violations. -- [x] 2.2 Add platform validator rules for manifest registration, allowed permissions/capabilities, unsafe string detection, duplicate-free lists, and registry response safety. -- [x] 2.3 Add service and repository behavior that registers a manifest as an installed game plugin while preserving existing metadata create/list/detail behavior. - -## 3. Platform Registry API - -- [x] 3.1 Add a manifest registration endpoint under the game plugin API surface using named DTOs and the core service. -- [x] 3.2 Extend game plugin list/detail responses with registry metadata required by marketplace and server creation workflows. -- [x] 3.3 Add platform API/service/validator tests for valid manifest registration, duplicate rejection, filtering, unsafe manifest rejection, and no raw internal/key fields in responses. - -## 4. Documentation And Handoff - -- [x] 4.1 Update platform route/protocol documentation and plugin documentation to describe manifest validation and registry registration boundaries. -- [x] 4.2 Add a fresh-chat handoff block for this change. - -## 5. Verification - -- [x] 5.1 Run `npm test` from `plugins/` and record evidence. -- [x] 5.2 Run `npm run typecheck` from `plugins/` and record evidence. -- [x] 5.3 Run `go test ./...` from `platform/` and record evidence. -- [x] 5.4 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.5 Run `openspec validate implement-plugin-registry-and-manifest-validation --strict` and record evidence. - -## Evidence - -- 2026-07-03: `npm test` from `plugins/` passed with 4 manifest/SDK tests. -- 2026-07-03: `npm run typecheck` from `plugins/` passed. -- 2026-07-03: `go test ./...` from `platform/` passed across api, config, domain, dto, model, repo, service, and validator packages. -- 2026-07-03: `scripts/check-structure.sh` passed with `structure check passed`. -- 2026-07-03: `openspec validate implement-plugin-registry-and-manifest-validation --strict` passed with `Change 'implement-plugin-registry-and-manifest-validation' is valid`. - -## Fresh-Chat Handoff - -```text -Implement OpenSpec change: implement-plugin-registry-and-manifest-validation - -Scope: -- Implement only openspec/changes/implement-plugin-registry-and-manifest-validation/. -- Preserve root ownership boundaries in AGENTS.md. -- Do not add billing, cloud host sales, agent-provider/cloud-provider workflows, or unrelated marketplace features. - -Read first: -- AGENTS.md -- openspec/changes/bootstrap-game-server-platform-architecture/proposal.md -- openspec/changes/bootstrap-game-server-platform-architecture/design.md -- openspec/changes/bootstrap-game-server-platform-architecture/specs/game-plugin-system/spec.md -- openspec/changes/bootstrap-game-server-platform-architecture/specs/game-server-platform-core/spec.md -- openspec/changes/implement-plugin-registry-and-manifest-validation/proposal.md -- openspec/changes/implement-plugin-registry-and-manifest-validation/design.md -- openspec/changes/implement-plugin-registry-and-manifest-validation/specs/plugin-registry-and-manifest-validation/spec.md -- openspec/changes/implement-plugin-registry-and-manifest-validation/tasks.md - -Required closure: -- Complete task checkboxes only after evidence exists. -- Run `npm test` and `npm run typecheck` from `plugins/`. -- Run `go test ./...` from `platform/`. -- Run `scripts/check-structure.sh`. -- Run `openspec validate implement-plugin-registry-and-manifest-validation --strict`. -- Stop after this change is closed; do not start the next backlog item unless explicitly asked. -``` diff --git a/openspec/changes/implement-production-operations-governance/.openspec.yaml b/openspec/changes/implement-production-operations-governance/.openspec.yaml deleted file mode 100644 index 0bd76e6..0000000 --- a/openspec/changes/implement-production-operations-governance/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-18 diff --git a/openspec/changes/implement-production-operations-governance/design.md b/openspec/changes/implement-production-operations-governance/design.md deleted file mode 100644 index 8730f3c..0000000 --- a/openspec/changes/implement-production-operations-governance/design.md +++ /dev/null @@ -1,80 +0,0 @@ -## Context - -Item 9 made the console truthful about API-backed operational state, current-session operations, and safe projections. The remaining production gap is not another dashboard facade; it is durable governance: admission control under capacity pressure, alert lifecycle closure, complete plugin lifecycle state, and real provider-backed AI assistance. The implementation must reuse Platform as the authority and Run as a channelized executor while keeping plugin pages and browser code away from raw secrets, direct sockets, host paths, and provider transport details. - -## Goals / Non-Goals - -**Goals:** - -- Govern server and Run capacity through Platform admission checks, safe endpoint capacity reports, queued/retrying jobs, and explicit capacity-denied responses. -- Persist alerts with source, severity, state, acknowledgement, scoped retry, resolution, suppression windows, audit links, and safe diagnostic text. -- Model complete plugin lifecycle state and compatibility gates in Platform, and drive lifecycle jobs through the existing job channel. -- Add real AI Provider invocation through a provider client that uses Platform-stored key/base URL material and never returns raw credentials or transport details. -- Require AI-generated config changes to produce a diff/recommendation that must be approved before any run-side write job is dispatched. -- Surface governance, alerts, lifecycle, and AI states in existing first-party pages without replacing the visual system or full-width management surfaces. -- Extend Run capacity/lifecycle contracts with bounded metadata only, preserving channel isolation and priority. - -**Non-Goals:** - -- No billing, cloud host sales, cloud/agent provider marketplace, or SaaS marketplace expansion. -- No arbitrary shell, direct Run socket/endpoint, host path projection, raw PID/socket/DSN/RCON, or raw credential/token/key/base URL exposure. -- No browser/plugin storage of provider secrets. -- No replacement of the five required first-party areas or the black-mecha/magical-girl theme system. - -## Decisions - -### Decision 1: Platform owns production admission decisions - -Run heartbeats report bounded capacity metadata. Platform combines that with server bindings, queued/running job pressure, endpoint capability availability, and configured limits before accepting lifecycle, dependency, backup, update, and plugin jobs. Rejected work returns a safe capacity or compatibility reason and records audit/alert evidence. - -Alternative considered: let Run reject after claim. Rejected because operators need immediate Platform feedback, and Run-only rejection creates noisy retry loops. - -### Decision 2: Alerts are durable state, not transient UI messages - -Capacity pressure, failed lifecycle work, stale endpoints, AI provider failures, plugin compatibility failures, and unsafe request denials create or update durable alert records. Alerts have explicit acknowledgement and resolution transitions with actor and audit metadata. Current-session UI operations remain separate and cannot close durable alerts by themselves. - -Alternative considered: derive alerts only from latest jobs/metrics. Rejected because acknowledgement, suppression, and resolution are production workflows that must survive restart. - -### Decision 3: Plugin lifecycle state is managed by Platform - -Marketplace enablement remains a catalog state, while lifecycle operations track installed plugin version, target server compatibility, dependency status, desired state, current state, upgrade/rollback availability, linked job IDs, and audit/alert summaries. Plugin manifests and bridge calls declare capabilities and purposes, but Platform authorizes and dispatches all jobs. - -Alternative considered: plugin pages manage lifecycle locally through bridge state. Rejected because that would bypass durable authorization and job/audit truth. - -### Decision 4: Real AI Provider calls use a redacted provider client - -Platform selects an active provider, decrypts key/base URL material inside the service boundary, sends bounded requests through a provider client, and returns only safe recommendation/diff/usage metadata. Tests and local mode can still use deterministic clients, but production code must support a real HTTP provider path with redacted failures. - -Alternative considered: expose base URLs or keys to plugin pages for direct calls. Rejected by the repository AI Provider rules and secret boundary. - -### Decision 5: AI config changes are two-step - -AI can suggest a structured diff for configuration or lifecycle inputs. Platform stores the diff preview and requires explicit approval before dispatching a run-side config write job. Approval is scoped to actor/server/plugin/config revision and stale approvals are denied. - -Alternative considered: dispatch config writes directly after AI completion. Rejected because AI suggestions must be reviewable before mutation. - -### Decision 6: Run receives bounded lifecycle and capacity metadata only - -Run protocol additions are limited to safe capacity dimensions, pressure reasons, lifecycle capability names, and logical job inputs. They do not carry raw host paths, process IDs, sockets, credentials, provider configuration, or browser/plugin transport material. - -Alternative considered: add a general operations channel. Rejected because existing control/job/log/artifact/game-client channels already define the isolation model. - -## Risks / Trade-offs - -- [Risk] Alert deduplication can hide repeated incidents. Mitigation: keep occurrence counts, last-seen timestamps, state transitions, and audit references. -- [Risk] Capacity admission can reject recoverable work too early. Mitigation: use explicit retry-after hints, scoped retry, and distinguish hard compatibility failures from temporary pressure. -- [Risk] Real provider calls can leak transport errors. Mitigation: redact provider failures and validate every response before returning it. -- [Risk] Lifecycle state overlaps existing marketplace state. Mitigation: keep marketplace catalog state separate from server/plugin lifecycle installations. -- [Risk] Broad verification may be expensive. Mitigation: add focused unit tests first, then run full platform_web, plugins, platform, run, structure, OpenSpec, diff, and browser checks before completion. - -## Migration Plan - -1. Add OpenSpec artifacts and validate strictly before implementation. -2. Add shared Platform domain/DTO/validator/model/repo/service support for capacity governance, alerts, plugin lifecycle, and AI diff approvals. -3. Add Platform API routes and route docs with named DTOs and OpenAPI-style handler comments. -4. Extend plugin manifest/SDK/bridge contracts and examples with lifecycle and mediated AI purpose metadata. -5. Extend Run protocol/runtime capacity and lifecycle metadata without crossing channel boundaries. -6. Update platform_web contracts/pages/theme/tests to surface capacity, alerts, lifecycle, and AI diff approval using existing first-party surfaces. -7. Run full verification and record evidence in this change's tasks. - -Rollback is additive: remove the new governance/lifecycle/alert/AI diff approval routes and UI panels, while preserving existing item 9 console behavior and earlier durable job/channel mechanics. diff --git a/openspec/changes/implement-production-operations-governance/proposal.md b/openspec/changes/implement-production-operations-governance/proposal.md deleted file mode 100644 index fd416c3..0000000 --- a/openspec/changes/implement-production-operations-governance/proposal.md +++ /dev/null @@ -1,30 +0,0 @@ -## Why - -The platform now has a real API-backed operations console and durable Run channels, but production operators still need stronger capacity governance, alert closure, full plugin lifecycle state, and a non-mock Platform-mediated AI Provider path. These areas were intentionally left as future work in prior changes and now need to become first-class, durable workflows without weakening the existing browser, plugin, Run, and secret boundaries. - -## What Changes - -- Add production capacity governance for Run endpoints and server instances: safe capacity reports, admission decisions, pressure alerts, and bounded retry/reconciliation. -- Add durable alert rules/events with acknowledgement, scoped retry, resolution, audit evidence, and UI surfacing across overview, server detail, maintenance, and operation history. -- Complete plugin lifecycle beyond marketplace state: install, enable, disable, upgrade, rollback, retire, dependency checks, compatibility gates, lifecycle jobs, and durable audit/alert hooks. -- Replace mock-only AI invocation with a real provider-client integration path that keeps keys and base URLs in Platform, validates plugin purposes, returns redacted recommendations, and requires reviewable diff approval before config writes. -- Extend Run channel contracts only with bounded capacity and lifecycle metadata; do not add direct browser-to-Run access or plugin-owned transports. -- Preserve the required first-party areas and the black-mecha / magical-girl crystal-moonlight operations console style. - -## Capabilities - -### New Capabilities - -- `production-operations-governance`: Platform-owned capacity governance, alert closure, complete plugin lifecycle, and real mediated AI Provider operations. - -### Modified Capabilities - -None. - -## Impact - -- Affects `platform/` domain, DTOs, validators, repositories, services, API routes, tests, and docs. -- Affects `platform_web/` API contracts, page contracts, first-party pages, shared operation components, theme styles, browser acceptance, and tests. -- Affects `plugins/` manifest schema, SDK/bridge contracts, examples, validation tests, and docs. -- Affects the independent `run/` protocol/runtime with bounded capacity/lifecycle metadata and tests. -- Does not add billing, cloud host sales, cloud/agent provider marketplace, arbitrary shell, direct Run socket/endpoint exposure, or raw credential projection. diff --git a/openspec/changes/implement-production-operations-governance/specs/production-operations-governance/spec.md b/openspec/changes/implement-production-operations-governance/specs/production-operations-governance/spec.md deleted file mode 100644 index 09b51bd..0000000 --- a/openspec/changes/implement-production-operations-governance/specs/production-operations-governance/spec.md +++ /dev/null @@ -1,109 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform governs production capacity - -Platform SHALL make admission and scheduling decisions using persisted server bindings, endpoint capability support, current Run capacity reports, durable job pressure, and configured safety limits before dispatching production lifecycle work. - -#### Scenario: Capacity accepts bounded work -- **WHEN** an authorized operator requests a plugin lifecycle, dependency, backup, update, or server lifecycle operation and an assigned endpoint is online with matching capabilities and available capacity -- **THEN** Platform MUST create one durable job with safe capacity metadata, audit the admission decision, and expose a safe job projection - -#### Scenario: Capacity denies pressure safely -- **WHEN** the assigned endpoint is offline, stale, missing capability support, over its configured running/queued limits, or under log/artifact/backlog pressure -- **THEN** Platform MUST reject or defer the request with a safe retry-after/capacity reason, MUST NOT fabricate success, and MUST NOT expose raw endpoint addresses, sockets, paths, credentials, leases, or session tokens - -### Requirement: Run reports bounded capacity without blocking priority channels - -Run SHALL report only bounded capacity dimensions on control heartbeat and job claim metadata, and SHALL keep control, job lifecycle, log ingest, artifact transfer, and optional game-client bridge channels independent. - -#### Scenario: Artifact backlog exists during heartbeat -- **WHEN** artifact or component transfer backlog is present -- **THEN** Run MUST continue sending control heartbeat and job ack/result/cancel/reconcile metadata without embedding artifact chunks, host paths, local file names, credentials, sockets, or provider configuration - -#### Scenario: Capacity report is sanitized -- **WHEN** Platform or browser views Run endpoint capacity -- **THEN** visible capacity MUST include only logical counts, percentages, pressure codes, and timestamps, never raw host paths, PIDs, sockets, DSNs, RCON values, secrets, leases, or direct endpoint URLs - -### Requirement: Alerts are durable and closeable - -Platform SHALL persist alert records for production incidents including capacity pressure, endpoint staleness, failed jobs, lifecycle failures, plugin compatibility failures, unsafe request denials, and AI provider failures. - -#### Scenario: Alert is acknowledged and resolved -- **WHEN** an authorized operator acknowledges and later resolves an active alert -- **THEN** Platform MUST update durable alert state, actor, timestamps, audit references, and safe resolution notes without mutating unrelated alerts - -#### Scenario: Alert retry remains scoped -- **WHEN** an operator retries a failed alert source such as endpoint capacity refresh, plugin lifecycle check, or AI provider test -- **THEN** only that source is retried, busy state prevents duplicate submission, and success/failure is reflected from Platform responses rather than local timers - -### Requirement: Plugin lifecycle is complete and platform-mediated - -Platform SHALL manage plugin lifecycle installations separately from marketplace catalog state, including install, enable, disable, upgrade, rollback, retire, dependency checks, compatibility gates, desired/current state, job linkage, audit history, and alert integration. - -#### Scenario: Plugin upgrade is approved -- **WHEN** an authorized operator approves an upgrade for a server-bound plugin installation with a compatible target version, satisfied dependency checks, and endpoint capability support -- **THEN** Platform MUST create a durable lifecycle job and transition state only from job results or reconciliation evidence - -#### Scenario: Plugin rollback is repeated -- **WHEN** rollback is retried with the same idempotency key and immutable inputs -- **THEN** Platform MUST return the original lifecycle/job result and MUST reject the same key with different immutable inputs - -#### Scenario: Unsafe lifecycle input is rejected -- **WHEN** plugin lifecycle metadata, manifest actions, bridge requests, or job inputs contain arbitrary shell, raw credentials, direct Run endpoints, host paths, PIDs, sockets, DSNs, RCON data, unknown capabilities, or undeclared AI purposes -- **THEN** validation MUST reject the request before registration or dispatch - -### Requirement: Plugins request AI only through Platform-mediated capability - -Plugin manifests, SDK calls, and platform-hosted plugin pages SHALL request AI assistance only through typed Platform bridge/API contracts that declare purpose, request ID, scoped input, and context references. - -#### Scenario: Declared AI purpose is invoked -- **WHEN** a plugin invokes a declared AI purpose through the Platform bridge -- **THEN** Platform MUST validate plugin installation state, route permission, server scope, purpose, payload bounds, provider availability, and unsafe text before invoking a provider client - -#### Scenario: Undeclared AI purpose is denied -- **WHEN** a plugin invokes an undeclared or currently unauthorized AI purpose -- **THEN** Platform MUST deny the request with a safe error and MUST NOT call a provider client - -### Requirement: Real AI Provider integration stays inside Platform - -Platform SHALL support a real provider client path that reads provider keys and base URL material only inside Platform service boundaries, invokes enabled providers with bounded requests, and returns redacted responses. - -#### Scenario: Real provider returns recommendation -- **WHEN** an enabled provider is selected and the provider client succeeds -- **THEN** Platform MUST return safe recommendation, model, usage, request ID, and optional diff preview metadata without raw API keys, bearer tokens, base URL secrets, transport headers, storage material, host paths, sockets, or credentials - -#### Scenario: Provider fails -- **WHEN** provider transport, authentication, rate limit, or model invocation fails -- **THEN** Platform MUST persist a safe audit/alert, return a redacted failure, and MUST NOT expose raw provider URL, headers, keys, request body secrets, or stack traces - -### Requirement: AI config changes require reviewable diff approval - -AI-generated configuration changes SHALL be persisted as reviewable recommendations or diff previews and SHALL require separate operator approval before Platform dispatches any run-side config write job. - -#### Scenario: Diff is approved -- **WHEN** an authorized operator approves a current, matching AI config diff preview for the same server, plugin, actor scope, and config revision -- **THEN** Platform MUST dispatch one bounded config write job and link the job, approval, audit event, and source AI request - -#### Scenario: Diff is stale or cancelled -- **WHEN** the config revision changes, the approval is cancelled, or the preview has expired -- **THEN** Platform MUST reject dispatch, preserve the recommendation for review history, and avoid run-side mutation - -### Requirement: Console surfaces governance without changing visual direction - -platform_web SHALL surface capacity governance, alert closure, plugin lifecycle, and AI diff approval in the existing first-party operations console using real Platform APIs, permissions, confirmation, busy state, scoped retry, and failure recovery. - -#### Scenario: Operator closes alert in the console -- **WHEN** an authorized operator acknowledges or resolves an alert -- **THEN** the UI MUST dispatch one Platform request, block duplicate submission while pending, refresh from persisted response, and show failure recovery without claiming local success - -#### Scenario: Narrow viewport renders governance surfaces -- **WHEN** the console is rendered at 390px in black-mecha or magical-girl theme -- **THEN** capacity, alerts, lifecycle, AI diff review, confirmations, and operation rows MUST remain readable, bounded, and theme-consistent without page-local global decoration - -### Requirement: Verification covers production governance boundaries - -The change SHALL include platform tests, run tests, plugin typecheck/tests/manifest validation, platform_web typecheck/tests/build, browser acceptance evidence, structure validation, strict OpenSpec validation, two-repository diff whitespace checks, and forbidden-field scans before tasks are marked complete. - -#### Scenario: Verification evidence is recorded -- **WHEN** implementation tasks are completed -- **THEN** `tasks.md` MUST record real command/browser evidence and MUST NOT mark verification complete until those checks pass diff --git a/openspec/changes/implement-production-operations-governance/tasks.md b/openspec/changes/implement-production-operations-governance/tasks.md deleted file mode 100644 index ed2afaa..0000000 --- a/openspec/changes/implement-production-operations-governance/tasks.md +++ /dev/null @@ -1,57 +0,0 @@ -## 1. OpenSpec and Architecture - -- [x] 1.1 Create proposal, design, spec, and task artifacts for production operations governance. -- [x] 1.2 Run `openspec validate implement-production-operations-governance --strict` before implementation. - -## 2. Platform Capacity, Alerts, and Lifecycle - -- [x] 2.1 Add domain/DTO/validator/model/repo/service support for production capacity governance and safe admission decisions. -- [x] 2.2 Add durable alert records with acknowledge, resolve, scoped retry, audit linkage, deduplication, and safe diagnostic text. -- [x] 2.3 Add complete plugin lifecycle installation state, compatibility gates, idempotency fences, upgrade/rollback/retire operations, and job/audit/alert integration. -- [x] 2.4 Add real AI Provider client integration plus redacted provider failure handling and alert/audit evidence. -- [x] 2.5 Add AI config diff preview/approval persistence and dispatch gating before run-side config write jobs. -- [x] 2.6 Update Platform API routes, route docs, protocol docs, and tests. - -## 3. Plugin SDK and Bridge - -- [x] 3.1 Extend manifest schema and examples with production lifecycle and AI purpose declarations. -- [x] 3.2 Extend SDK/bridge contracts to request lifecycle/AI through Platform-mediated capabilities only. -- [x] 3.3 Add validation/typecheck/tests proving unsafe lifecycle input and raw credential/direct Run fields are rejected. - -## 4. Run Protocol and Runtime - -- [x] 4.1 Extend Run protocol capacity reports and job metadata with bounded pressure/lifecycle fields only. -- [x] 4.2 Update Run runtime capacity reporting and lifecycle handling without blocking control/job/log/artifact channels. -- [x] 4.3 Add Run tests for capacity sanitization, channel isolation under backlog, and lifecycle metadata validation. - -## 5. platform_web Console - -- [x] 5.1 Add typed API/contracts for capacity governance, alerts, plugin lifecycle, AI diff preview, and approval. -- [x] 5.2 Surface alerts and capacity on overview, server detail, maintenance, and operation history with confirmation, busy state, scoped retry, persisted responses, and failure recovery. -- [x] 5.3 Surface full plugin lifecycle and AI diff approval in existing first-party pages using shared themed surfaces. -- [x] 5.4 Add tests for permissions, duplicate prevention, failed recovery, forbidden-field omission, and 390px theme-safe rendering. - -## 6. Verification - -- [x] 6.1 Run platform Go tests. -- [x] 6.2 Run independent Run Go tests. -- [x] 6.3 Run plugins typecheck, tests, and manifest validation. -- [x] 6.4 Run platform_web typecheck, tests, and production build. -- [x] 6.5 Run browser acceptance for the five first-party areas, server detail, alerts, plugin lifecycle, AI diff approval, desktop and 390px, black-mecha and magical-girl. -- [x] 6.6 Scan rendered outputs for raw key/token/secret/base URL secret/path/PID/socket/credential/DSN/RCON/direct Run endpoint fragments. -- [x] 6.7 Run `scripts/check-structure.sh`, `openspec validate implement-production-operations-governance --strict`, and both repository `git diff --check`. -- [x] 6.8 Record real verification evidence in this file before marking verification tasks complete. - -## Verification Evidence - -- 2026-07-18 19:02 CST: `cd platform && go test ./...` passed for all Platform packages; `cd /Users/tasia/Desktop/code/run && go test ./...` passed for the independent Run repository. -- `cd plugins && npm run typecheck`, `npm test -- --run`, and `npm run validate:manifest` passed: 1 test file / 19 tests and all three example manifests validated. The manifest command was rerun outside the filesystem sandbox because `tsx` requires a local IPC pipe. -- `cd platform_web && npm run typecheck`, `npm test -- --run`, and `npm run build` passed: 26 test files / 135 tests, 1,829 production modules transformed, and the production bundle completed successfully. -- `scripts/local-debug-smoke.sh` and `npm --prefix platform_web run acceptance:browser` passed against the real local Platform and independent Run stack rooted at `/private/tmp/browser-local-debug-acceptance-10o`. `node --check platform_web/acceptance/browser-acceptance.mjs` and `bash -n scripts/local-debug-env.sh scripts/local-debug-smoke.sh scripts/local-debug-start.sh` also passed. -- Capacity evidence recorded a persisted `denied` admission with bounded `queue.limit` and `capability.missing` pressure codes plus a linked durable alert and audit event. Alert cancellation preserved `active`; confirmed acknowledgement persisted `acknowledged`, actor, timestamp, and audit linkage. -- Plugin lifecycle install and enable each produced durable Platform jobs. The persisted installation reached the requested enabled state with linked job and audit IDs; browser scans passed after the lifecycle interactions. -- AI diff cancellation preserved `pending`; explicit approval persisted `approved` and dispatched exactly one `config.write` job. Independent Run completed that job as `succeeded` with a safe `file.write` result at version 2 and an `atomic compare-and-swap file write` audit summary; Platform configuration advanced from version 1 to 2 before the subsequent real `process.stop` request. -- Browser acceptance covered 首页、服务器管理、插件市场、用户管理、AI 提供商管理, server detail, maintenance/operation history, alert closure, plugin lifecycle, and AI diff approval in four scenarios: 1440x960 and 390x844 for `mecha-black` and `magical-girl`. All 38 route/interaction checks reported zero horizontal overflow, zero overlapping controls, and no tiny visible text boxes. -- API and rendered-output scans passed for raw key/token/secret/base URL secret, host path, PID, socket, credential, DSN, RCON, session/direct Run endpoint, and plugin-owned transport fragments. Confirm/cancel, busy duplicate prevention, persisted terminal state, real failure recovery, and scoped retry evidence are included in the structured artifact. -- `scripts/check-structure.sh`, `openspec validate implement-production-operations-governance --strict`, the main repository `git diff --check`, and the independent Run repository `git diff --check` passed. These gates were rerun after recording this evidence. -- Structured browser evidence: `/private/tmp/browser-local-debug-acceptance-10o/browser-acceptance/browser-acceptance-evidence.json` and `/private/tmp/browser-local-debug-acceptance-10o/browser-acceptance/item-10-evidence.json`. Durable Platform/Run result snapshot: `/private/tmp/browser-local-debug-acceptance-10o/platform/metadata.json`. diff --git a/openspec/changes/implement-real-game-plugin-lifecycle-proof/.openspec.yaml b/openspec/changes/implement-real-game-plugin-lifecycle-proof/.openspec.yaml deleted file mode 100644 index 8cceb8d..0000000 --- a/openspec/changes/implement-real-game-plugin-lifecycle-proof/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-08 diff --git a/openspec/changes/implement-real-game-plugin-lifecycle-proof/design.md b/openspec/changes/implement-real-game-plugin-lifecycle-proof/design.md deleted file mode 100644 index 8a4dad5..0000000 --- a/openspec/changes/implement-real-game-plugin-lifecycle-proof/design.md +++ /dev/null @@ -1,80 +0,0 @@ -## Context - -The current architecture stream has package-level evidence for platform APIs, run channels, plugin manifests, SDK bridge envelopes, and the management frontend. The latest baseline still found a critical product gap: it could not prove that one local game management plugin can create and manage multiple real server instances through the intended platform-mediated path. - -This proof spans all four product roots. `plugins/` must declare and request lifecycle capabilities without owning transport. `platform/` must authorize plugin lifecycle requests, create server records, dispatch bounded jobs, persist state, and expose safe API responses. `run/` must execute or simulate local lifecycle jobs through its channelized executor contract and return observable results. `platform_web/` must let an authorized user install/use the plugin and inspect separate server instances without showing raw host paths, credentials, sockets, or run transport details. - -## Goals / Non-Goals - -**Goals:** - -- Prove one installed local game management plugin can create at least two independent server instances. -- Prove install/create/start/stop/status/log/artifact lifecycle operations are routed through platform APIs and run jobs, not direct browser/plugin access to run. -- Prove each instance has independent identity, lifecycle state, operation history, logs/artifacts where applicable, and browser-visible controls. -- Prove plugin manifest permissions and SDK bridge envelopes reject direct run URLs, host paths, raw credentials, raw AI keys, and undeclared lifecycle actions. -- Provide repeatable API, run, plugin, frontend, and browser verification commands. - -**Non-Goals:** - -- No billing, cloud host sales, agent-provider/cloud-provider workflows, or unrelated marketplace behavior. -- No production-grade game server hosting, cloud provisioning, external orchestrator, or remote game binary distribution. -- No direct browser-to-run or plugin-to-run transport. -- No new raw host path, raw socket, raw credential, or raw AI-key exposure. -- No broad redesign of the magical crystal-moonlight frontend style. - -## Decisions - -### Decision 1: Use one first-party local proof plugin - -The implementation will use the existing local example/dev game plugin as the proof target or evolve it into a clearly named local lifecycle proof plugin. The plugin declares lifecycle actions and platform-mediated capabilities in its manifest, and its SDK calls produce bounded platform bridge requests. - -Alternative considered: introduce several plugins for different games. Rejected because the stream needs one focused proof that the lifecycle path is real before multiplying game-specific scope. - -### Decision 2: Platform owns lifecycle authority and instance records - -The platform remains the authority for plugin installation state, server instance records, lifecycle authorization, job creation, audit events, and safe response DTOs. Plugin requests name logical server/plugin/action identifiers; platform translates them into jobs for run and stores resulting state. - -Alternative considered: allow plugin pages to call run endpoints directly for local development. Rejected because it violates the required channel boundaries and would make browser proof unsafe. - -### Decision 3: Multi-instance proof requires independent state and evidence - -The proof is not accepted unless the same installed plugin creates at least two server instances and can start/stop them independently. Evidence must include distinct IDs/names, operation history per instance, lifecycle state projection, and browser-visible separation. - -Alternative considered: create one server and assume the code generalizes. Rejected because the baseline gap is specifically multi-instance real operation. - -### Decision 4: Run proof can use bounded local lifecycle execution - -The run-side executor may use a deterministic local fixture command or safe simulated local game process when no real game binary is available, but it must still travel through the run job channel and return job ack/result/state evidence. Any fixture must be explicit and must not expose raw host paths to plugins or platform_web. - -Alternative considered: require a full real game server binary immediately. Rejected because the architecture proof is about platform-mediated lifecycle wiring and instance isolation, not a specific commercial game runtime. - -### Decision 5: Browser acceptance must be API-backed - -The browser walkthrough must use a real platform session and local stack. Local auth fallback, static seed-only data, and demo-only plugin controls cannot satisfy this proof. The walkthrough must record the stack commands, pages visited, actions taken, visible state, and unsafe-field checks. - -Alternative considered: accept unit and integration tests only. Rejected because this stream requires browser evidence for frontend-facing behavior. - -## Risks / Trade-offs - -- [Risk] Local stack setup may remain brittle. Mitigation: tasks require exact commands, health checks, and explicit blocker recording if a service cannot bind or authenticate. -- [Risk] A simulated local lifecycle fixture could be mistaken for production hosting. Mitigation: name it as a local proof fixture and require platform/run channel evidence rather than game-specific claims. -- [Risk] Plugin bridge expansion could accidentally expose transport details. Mitigation: add negative tests for direct run URLs, host paths, raw credentials, sockets, and raw AI keys. -- [Risk] Multi-instance state can collapse into shared mutable demo data. Mitigation: require two instances with independent IDs, operation histories, and state transitions. -- [Risk] Browser proof can pass against fallback data. Mitigation: require API-backed login, platform responses, and no `本地会话`/demo fallback classification for accepted proof. - -## Migration Plan - -1. Extend the local proof plugin manifest and SDK/page code to declare lifecycle actions and platform-mediated capability envelopes. -2. Add platform service/API support or repair existing endpoints for plugin-mediated multi-instance create/install/start/stop/status operations. -3. Add run-side lifecycle handling needed to acknowledge jobs, execute bounded local fixture operations, and return instance-specific results. -4. Update platform_web server/plugin flows to expose the proof actions and state without changing the visual system. -5. Add automated tests across plugin, platform, run, and platform_web. -6. Run the full local stack and browser walkthrough, recording exact evidence before marking tasks complete. - -Rollback before acceptance is to remove the proof plugin lifecycle declarations, platform/run/frontend implementation changes, and any verification fixtures added by this change. - -## Open Questions - -- Whether the local lifecycle fixture should be a no-op process, a tiny managed process, or an existing example game command. -- Whether the browser walkthrough should use docker-compose or separate local commands as the primary evidence path. -- Whether the proof report should be a standalone artifact under this change or embedded only in `tasks.md` evidence. diff --git a/openspec/changes/implement-real-game-plugin-lifecycle-proof/proposal.md b/openspec/changes/implement-real-game-plugin-lifecycle-proof/proposal.md deleted file mode 100644 index 9cd71a3..0000000 --- a/openspec/changes/implement-real-game-plugin-lifecycle-proof/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -The baseline proof showed substantial package-level platform/run/plugin evidence, but it did not prove that a real local game management plugin can create and manage multiple server instances end to end. This change turns that gap into one focused implementation proof before the stream adds more surface area. - -## What Changes - -- Add a local game plugin lifecycle proof that creates and manages multiple server instances through platform-mediated contracts only. -- Implement the missing platform/run/plugin/frontend wiring needed for one installed local plugin to request create/install/start/stop/status/log/artifact operations without direct run access. -- Verify multiple server instances can be created from one game management plugin and tracked independently through platform storage, run jobs, operation status, and browser-visible state. -- Add API, run, plugin SDK/manifest, and platform_web tests for lifecycle permissions, bridge envelopes, multi-instance isolation, and safe field redaction. -- Require a browser walkthrough proving the lifecycle flow is real, API-backed, and free of raw host paths, raw credentials, direct sockets, raw AI keys, or plugin-owned run transport details. - -## Capabilities - -### New Capabilities - -- `real-game-plugin-lifecycle-proof`: Proves a local game management plugin can create and manage multiple server instances only through platform-mediated platform/run capabilities. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `plugins/` manifests, SDK/example plugin behavior, and manifest validation tests for lifecycle capability declarations. -- Affects `platform/` plugin lifecycle APIs, server instance orchestration, job dispatch, audit/event evidence, and safe response DTOs. -- Affects `run/` lifecycle execution handling, job acknowledgement/result projection, and instance-specific isolation evidence. -- Affects `platform_web/` server management and plugin marketplace/detail surfaces needed to trigger and inspect the proof in a browser. -- Does not add billing, cloud host sales, agent-provider/cloud-provider workflows, unrelated marketplace behavior, direct browser/plugin access to run, raw host path exposure, raw run credentials, direct sockets, or raw AI keys. diff --git a/openspec/changes/implement-real-game-plugin-lifecycle-proof/specs/real-game-plugin-lifecycle-proof/spec.md b/openspec/changes/implement-real-game-plugin-lifecycle-proof/specs/real-game-plugin-lifecycle-proof/spec.md deleted file mode 100644 index b775fe7..0000000 --- a/openspec/changes/implement-real-game-plugin-lifecycle-proof/specs/real-game-plugin-lifecycle-proof/spec.md +++ /dev/null @@ -1,56 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform-mediated plugin lifecycle authority -The system SHALL route local game plugin lifecycle requests through platform-owned authorization, server instance records, and run job dispatch. Browser code and plugin code MUST NOT connect directly to run endpoints, raw sockets, raw host paths, or raw credentials. - -#### Scenario: Plugin requests server creation through platform -- **WHEN** an authorized user invokes a declared plugin lifecycle action to create a server instance -- **THEN** the platform MUST validate the installed plugin, declared capability, user authorization, and request payload before creating the server instance and dispatching any run job - -#### Scenario: Direct run access is rejected -- **WHEN** a plugin manifest, plugin page, SDK request, or browser-visible payload attempts to use a direct run URL, raw socket, host path, bearer credential, password, or undeclared transport detail -- **THEN** validation MUST reject the request or redact the unsafe field before it reaches platform_web or plugin code - -### Requirement: One plugin manages multiple server instances -The system SHALL allow one installed local game management plugin to create and manage at least two independent server instances through platform-mediated lifecycle capabilities. - -#### Scenario: Create two instances from one plugin -- **WHEN** an authorized user creates two server instances using the same installed local game management plugin -- **THEN** the platform MUST persist two distinct server instance records with independent IDs, names, lifecycle state, plugin association, and operation history - -#### Scenario: Start and stop one instance independently -- **WHEN** the user starts one plugin-created server instance and leaves the second instance stopped -- **THEN** the run job result and platform state projection MUST show only the targeted instance as running while the other instance remains stopped - -#### Scenario: Stop does not affect sibling instance -- **WHEN** the user stops one running plugin-created server instance while another sibling instance remains running -- **THEN** the platform MUST preserve the sibling instance state and MUST record the stop operation only against the targeted instance - -### Requirement: Run lifecycle jobs provide observable proof -The run executor SHALL process plugin-mediated lifecycle jobs through the existing job channel and return acknowledgement, progress or result, and instance-specific state evidence to platform. - -#### Scenario: Lifecycle job acknowledgement and result -- **WHEN** platform dispatches a plugin-mediated install, start, or stop lifecycle job to run -- **THEN** run MUST acknowledge the job and return a bounded result that platform can attach to the correct server instance operation history - -#### Scenario: Instance-specific logs or artifacts -- **WHEN** a lifecycle operation produces logs or artifacts for a server instance -- **THEN** platform MUST expose only logical log/artifact references associated with that instance and MUST NOT expose raw run filesystem paths or transport credentials - -### Requirement: Browser walkthrough proves real API-backed lifecycle behavior -The implementation SHALL include a browser walkthrough that proves the plugin lifecycle flow uses a real API-backed session and not local fallback or seed-only demo state. - -#### Scenario: Browser creates and controls plugin instances -- **WHEN** the walkthrough logs in with an API-backed authorized user, opens the plugin/server management surface, creates two instances, starts one, stops it, and inspects operation history -- **THEN** the visible UI MUST show API-backed lifecycle state for each instance, distinct operation evidence, and no local fallback session indicator - -#### Scenario: Browser unsafe-field inspection -- **WHEN** the walkthrough inspects plugin marketplace, server list, server detail, operation history, log, artifact, and plugin bridge visible states -- **THEN** the visible UI MUST NOT contain raw host paths, raw run credentials, direct run sockets, bearer tokens, raw AI keys, or plugin-owned transport details - -### Requirement: Verification commands cover all roots -The change SHALL provide concrete verification commands for platform, run, platform_web, and plugins, plus strict OpenSpec validation and structure checks. - -#### Scenario: Verification suite passes before completion -- **WHEN** implementation tasks are marked complete -- **THEN** the recorded evidence MUST include passing platform tests, run tests, plugin typecheck/tests/manifest validation, platform_web typecheck/tests/build, `scripts/check-structure.sh`, `openspec validate implement-real-game-plugin-lifecycle-proof --strict`, and the browser walkthrough commands/results diff --git a/openspec/changes/implement-real-game-plugin-lifecycle-proof/tasks.md b/openspec/changes/implement-real-game-plugin-lifecycle-proof/tasks.md deleted file mode 100644 index 4630a7f..0000000 --- a/openspec/changes/implement-real-game-plugin-lifecycle-proof/tasks.md +++ /dev/null @@ -1,92 +0,0 @@ -## 1. Plugin Lifecycle Contract - -- [x] 1.1 Extend the local proof plugin manifest to declare platform-mediated lifecycle actions for create/install/start/stop/status/log/artifact operations. -- [x] 1.2 Add plugin manifest validation tests that accept declared lifecycle capabilities and reject direct run URLs, raw sockets, host paths, bearer credentials, passwords, raw AI keys, and undeclared transport details. -- [x] 1.3 Extend plugin SDK/example bridge envelopes so lifecycle requests carry only logical plugin, server, action, config, log, artifact, and AI capability references. -- [x] 1.4 Run `cd plugins && npm run typecheck && npm run test && npm run validate:manifest` and record evidence. - -## 2. Platform-Mediated Lifecycle API - -- [x] 2.1 Add or repair platform DTOs, validators, domain types, repository/service methods, and API handlers for plugin-mediated multi-instance create/install/start/stop/status operations. -- [x] 2.2 Ensure platform owns authorization, server instance persistence, plugin installation checks, lifecycle job creation, state projection, audit events, and safe response DTOs. -- [x] 2.3 Add platform tests proving one installed plugin can create at least two server instances with distinct IDs, names, lifecycle states, plugin associations, and operation histories. -- [x] 2.4 Add negative platform tests proving browser/plugin payloads cannot expose or submit raw host paths, direct run sockets, bearer tokens, passwords, raw AI keys, or undeclared lifecycle actions. -- [x] 2.5 Run `cd platform && go test ./... -count=1` and record evidence. - -## 3. Run Lifecycle Execution Proof - -- [x] 3.1 Add or repair run-side lifecycle handling for plugin-mediated install/start/stop jobs using scoped logical templates under `RUN_WORKSPACE_ROOT`. -- [x] 3.2 Ensure run acknowledges lifecycle jobs, reports bounded progress/result metadata, preserves per-instance isolation, and rejects unsafe command templates, absolute paths, parent traversal, shell launchers, credentials, and direct sockets. -- [x] 3.3 Add run tests proving start/stop on one instance does not mutate sibling instance state or block job result submission. -- [x] 3.4 Run `cd run && go test ./... -count=1` and record evidence. - -## 4. Platform Web Proof Surface - -- [x] 4.1 Update platform_web API types/client methods, schemas, route/page contracts, and components needed to trigger plugin-mediated lifecycle actions from server management or plugin detail surfaces. -- [x] 4.2 Preserve the magical-girl crystal-moonlight console style and avoid generic opaque SaaS restyling while adding lifecycle controls and operation status. -- [x] 4.3 Add frontend tests for API-backed plugin lifecycle controls, two-instance separation, operation history rendering, role access, and unsafe-field redaction. -- [x] 4.4 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. - -## 5. Local Full-Stack Proof - -- [x] 5.1 Start a local API-backed proof stack and record exact commands, including platform, run worker, and frontend commands such as `PLATFORM_ADDR=127.0.0.1:18080 PLATFORM_STORAGE_BACKEND=file PLATFORM_DATA_DIR=/private/tmp/browser-platform-lifecycle-proof PLATFORM_METADATA_PATH=/private/tmp/browser-platform-lifecycle-proof/metadata.json PLATFORM_LOG_BODY_BACKEND=file PLATFORM_LOG_DIR=/private/tmp/browser-platform-lifecycle-proof/logs go run ./cmd/platform`, `RUN_MODE=worker RUN_PLATFORM_URL=http://127.0.0.1:18080 RUN_WORKSPACE_ROOT=/private/tmp/browser-run-lifecycle-proof/workspaces RUN_SPOOL_ROOT=/private/tmp/browser-run-lifecycle-proof/spool go run ./cmd/run`, and `cd platform_web && PLATFORM_API_PROXY=http://127.0.0.1:18080 VITE_PLATFORM_API_BASE_URL=/api/v1 npm run dev -- --port 5173`. -- [x] 5.2 Verify platform health, run registration/heartbeat, plugin installation data, and API-backed login before browser walkthrough; record exact curl or test commands used. -- [x] 5.3 In a browser with a real API-backed platform administrator session, open 首页、服务器管理、插件市场、用户管理、AI 提供商管理 and confirm the session is not local fallback. -- [x] 5.4 In the browser, use one installed local game management plugin to create two server instances, start one, verify the sibling remains stopped, stop the targeted instance, and inspect per-instance operation history. -- [x] 5.5 In the browser, inspect plugin marketplace/detail, server list/detail, operation history, log/artifact references, and plugin bridge output to verify no raw host paths, run credentials, direct sockets, bearer tokens, raw AI keys, or plugin-owned transport details are visible. - -## 6. Final Verification and Stream Handoff - -- [x] 6.1 Record implementation evidence in this tasks file only after each command or walkthrough has actually run. -- [x] 6.2 Run `scripts/check-structure.sh` and record evidence. -- [x] 6.3 Run `openspec validate implement-real-game-plugin-lifecycle-proof --strict` and record evidence. -- [x] 6.4 Update `openspec/changes/architecture-delivery-stream/delivery-plan.md` to mark `fix-env-profile-settings` complete, mark `implement-real-game-plugin-lifecycle-proof` complete only after evidence exists, and leave the next queue item pending. -- [x] 6.5 Update `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` with the next implementation/generator handoff after this change closes. - -## Evidence - -- Plugin contract: - - `cd plugins && npm run typecheck` passed. - - `cd plugins && npm run test` passed: `tests/manifest-validation.test.ts` passed 11 tests. - - `cd plugins && npm run validate:manifest` passed after escalation for `tsx` IPC pipe creation: `validated examples/dev-game-plugin/manifest.json`. - - `plugins/examples/dev-game-plugin/manifest.json` declares install/start/stop/restart/status lifecycle actions, `jobs.dispatch`, log/file/artifact/AI bridge actions, and platform-mediated permissions. - - `plugins/sdk/index.ts` includes `PluginLifecycleDispatchPayload` and `createLifecycleDispatchRequest(...)` for `jobs.dispatch` lifecycle envelopes containing only logical plugin/server/action/capability/config/idempotency references. - - `plugins/tests/manifest-validation.test.ts` asserts lifecycle dispatch envelopes do not contain direct `http://`, `unix://`, `/Users/`, `Bearer `, or `sk-` content. - -- Platform lifecycle API: - - `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./api -run TestPluginBridgeExecuteAPI -count=1` passed. - - `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` passed for `api`, `config`, `domain`, `dto`, `model`, `repo`, `service`, and `validator`. - - `platform/service/server_lifecycle_test.go` includes `TestCoreServicePluginLifecycleManagesMultipleInstancesIndependently`, creating `server-alpha` and `server-beta` from one plugin, starting/stopping only alpha, and verifying beta remains unchanged. - - `platform/api/resource_handlers_test.go` includes `jobs.dispatch` lifecycle bridge execution coverage, action/capability mismatch denial, unsafe payload rejection, and forbidden-fragment response checks. - -- Run lifecycle execution: - - Initial sandbox run of `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` was blocked by `httptest` loopback bind permissions. - - Escalated rerun of `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./... -count=1` passed for `api`, `config`, `protocol`, `runtime`, and `spool`. - - `run/runtime/lifecycle_test.go` includes scoped template execution, unsafe template rejection, workspace escape rejection, cancellation, and sibling workspace isolation. - -- Platform web proof surface: - - `cd platform_web && npm run typecheck` passed. - - `cd platform_web && npm test` passed: 11 files, 49 tests. - - `cd platform_web && npm run build` passed: Vite built `dist/index.html`, CSS, and JS assets. - - `platform_web/pages/ServerDetailPage.test.tsx` verifies plugin lifecycle controls call `startServerInstance` / `stopServerInstance`, skip install/restart/status controls, avoid generic `process.start` / `process.stop` job creation, and keep bridge/lifecycle output on platform-owned references. - -- Controlled local full-stack proof: - - Used controlled stack after an existing `127.0.0.1:18080` process became unreachable despite still holding the port. - - Platform command run from `platform/`: `PLATFORM_ADDR=127.0.0.1:18082 PLATFORM_STORAGE_BACKEND=file PLATFORM_DATA_DIR=/private/tmp/browser-platform-lifecycle-proof-controlled PLATFORM_METADATA_PATH=/private/tmp/browser-platform-lifecycle-proof-controlled/metadata.json PLATFORM_LOG_BODY_BACKEND=file PLATFORM_LOG_DIR=/private/tmp/browser-platform-lifecycle-proof-controlled/logs GOCACHE=/private/tmp/browser-go-build-cache go run ./cmd/platform`. - - Run worker command run from `run/`: `RUN_MODE=worker RUN_PLATFORM_URL=http://127.0.0.1:18082 RUN_WORKSPACE_ROOT=/private/tmp/browser-run-lifecycle-proof/workspaces RUN_SPOOL_ROOT=/private/tmp/browser-run-lifecycle-proof/spool RUN_POLL_INTERVAL_MS=250 RUN_HEARTBEAT_INTERVAL_MS=1000 RUN_MAX_JOBS=4 GOCACHE=/private/tmp/browser-go-build-cache go run ./cmd/run`. - - Frontend command run from `platform_web/`: `PLATFORM_API_PROXY=http://127.0.0.1:18082 VITE_PLATFORM_API_BASE_URL=/api/v1 npm run dev -- --port 5175`. - - Health/login/run checks passed with `curl` against `127.0.0.1:18082`: `/healthz` returned `{"service":"platform","status":"ok","version":"0.1.0-dev"}`, `/api/v1/run/endpoints` returned online `run-local` with `process.install`, `process.start`, and `process.stop`, and `/api/v1/auth/login` authenticated `operator.local@example.test / operator-local` as `user-admin`. - - Registered installed proof plugin `game.lifecycle-proof@0.1.1` with lifecycle actions install/start/stop/restart/status, bridge actions server.instances.read/jobs.dispatch/logs.query/artifacts.open/files.request/ai.invoke, declared permissions for lifecycle/files/logs/artifacts/AI, and run-required capabilities narrowed to worker-real `process.install`, `process.start`, `process.stop`. - - Created two server instances through platform workflow: `proof-alpha` and `proof-beta`; both install jobs completed via run worker and both reached `ready`. - - Browser walkthrough on `http://127.0.0.1:5175/` logged in with the API-backed platform administrator session and opened 首页、服务器管理、插件市场、用户管理、AI 提供商管理. Each page reported `hasLocalFallback: false` and no visible forbidden fragments among `/Users/`, `/private/`, `unix://`, `tcp://`, `Bearer `, `sk-`, `password=`, `apiKeyRef`, or `rawApiKey`. - - Browser server detail walkthrough opened `#/servers/proof-alpha`, confirmed `Proof Alpha` was ready with start enabled, clicked `启动`, confirmed the dialog, and observed a visible `process.start` queued operation. - - API proof after UI start showed `proof-alpha` state `running`, `proof-beta` state `ready`, alpha start job `server-lifecycle:proof-alpha:start:3981de20495ff68b` succeeded with `process.start completed`, and beta had only its install job. - - Browser detail walkthrough then refreshed `proof-alpha`, confirmed it was running with stop enabled, clicked `停止`, confirmed the dialog, and observed a visible `process.stop` queued operation. - - API proof after UI stop showed `proof-alpha` state `stopped`, `proof-beta` still `ready`, alpha install/start/stop jobs all succeeded, and beta still had only its install job. - - Browser walkthrough visible surfaces did not expose raw host paths, run credentials, direct sockets, bearer tokens, raw AI keys, or plugin-owned transport details. - -- Final gates and stream handoff: - - `scripts/check-structure.sh` passed with `structure check passed`. - - `openspec validate implement-real-game-plugin-lifecycle-proof --strict` passed with `Change 'implement-real-game-plugin-lifecycle-proof' is valid`; the process exited 0. PostHog telemetry flush reported `ENOTFOUND edge.openspec.dev`, which did not affect validation. - - `openspec/changes/architecture-delivery-stream/delivery-plan.md` now marks `implement-real-game-plugin-lifecycle-proof` complete and `harden-log-artifact-channel-isolation` active. - - `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` now points the next implementation chat at `harden-log-artifact-channel-isolation`. diff --git a/openspec/changes/implement-real-process-supervision-and-config-file-execution/.openspec.yaml b/openspec/changes/implement-real-process-supervision-and-config-file-execution/.openspec.yaml deleted file mode 100644 index ff5f854..0000000 --- a/openspec/changes/implement-real-process-supervision-and-config-file-execution/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-17 diff --git a/openspec/changes/implement-real-process-supervision-and-config-file-execution/design.md b/openspec/changes/implement-real-process-supervision-and-config-file-execution/design.md deleted file mode 100644 index aa918b3..0000000 --- a/openspec/changes/implement-real-process-supervision-and-config-file-execution/design.md +++ /dev/null @@ -1,86 +0,0 @@ -## Context - -Platform already persists server runtime profiles/bindings and durable Job scheduling metadata. Run already persists active assignments and pending terminal results, reconciles them after session rotation, signs Run-service requests, and keeps control/jobs/logs/artifacts on separate routes. The missing execution layer is narrower but security-sensitive: lifecycle start/stop are currently one-shot `exec.CommandContext` calls, config reads are platform-derived, and config/file jobs do not carry durable approved bytes or produce typed execution results. - -The independent Run repository must remain separate. Browser/plugin callers name only a server, declared capability, logical target, and scoped input/artifact reference. Platform remains the authority for ownership, profile selection, approval, job attempt/session fencing, and safe projection. Run alone maps the logical scope into its private workspace and may persist PID or filesystem details, none of which can cross into platform_web or plugin DTOs. - -## Goals / Non-Goals - -**Goals:** - -- Supervise one declared local game process per server/profile scope with real spawn, liveness, graceful stop, exit observation, idempotent operations, and restart reconciliation. -- Execute approved config/file reads and writes with strict containment, symlink/device rejection, bounded I/O, atomic replacement, and compare-and-swap version/checksum semantics. -- Preserve private execution input and typed result state across Platform/Run restarts without weakening task 04 lease, attempt, session-generation, signature, cancellation, or reconciliation rules. -- Project only safe process state, exit classification, config/file version, checksum, size, and audit summary to authorized owners/admins and platform_web. -- Keep current visual language and existing 401/403 handling. - -**Non-Goals:** - -- General shell execution, arbitrary interpreters, host path selection, direct sockets, credential injection, multiple unmanaged processes per server, or OS service/container orchestration. -- Durable log/artifact/metric/backup storage, remote FTP/rsync/database/RCON adapters, dependency installation, Run self-update, client-manager lifecycle, production scaling/alerts/plugin lifecycle, or real AI-provider integration. -- Claiming that example action declarations install or ship commercial game binaries. - -## Decisions - -### Decision 1: Extend the durable Job aggregate with private execution input and typed result - -`Job` will persist a nested execution input containing the workspace profile key, approved content, expected version/checksum, and bounded read limit, plus a nested typed result containing kind, process state, exit classification, version, checksum, size, and audit summary. Model conversions and file/MySQL snapshot tests will cover the fields. The user `JobResponse` will omit approved content and expose only the safe result subset; the signed Run assignment carries the private input only after a matching claim. - -This is preferred over an in-memory input map because queued/retrying work must survive Platform restart. A separate input repository was considered, but the input has the same lifecycle, idempotency, ownership, and retention as its Job and would add cross-record transaction failure without providing reuse. - -### Decision 2: Resolve lifecycle action ref and workspace scope at dispatch - -Platform will load the server's persisted runtime binding and selected plugin lifecycle profile, verify the requested capability/action is declared, set the Job target to that action's relative JSON ref, and set the private workspace scope to the selected profile key. Config/file dispatch uses the same binding-derived scope and validates the endpoint, plugin permission, server ownership, logical target, and scoped input/artifact ref before Job creation. - -This replaces the current mismatch where lifecycle jobs send the profile key as `targetKey` while Run expects an action file ref. Platform will never send a host path or binding value to browser/plugin callers. - -### Decision 3: Typed action files distinguish one-shot and supervised operations - -Run will decode action JSON with unknown-field rejection, bounded size, and an explicit action matching the Job capability. `start` requires a workspace-relative executable key and argument vector; `stop` and `status` operate only on the persisted supervised identity for that server/profile; `install` remains a bounded one-shot declaration and does not represent dependency installation. Executable resolution rejects symlinks, non-regular/non-executable files, shells, interpreter escape forms, unsafe environment names/values, and paths outside the scoped workspace. - -This keeps plugins declarative and supports actual process execution without accepting shell source or a generic command string. Keeping the existing unrestricted PATH lookup was rejected because names such as interpreters can turn a safe-looking vector into arbitrary execution. - -### Decision 4: Run owns a private process journal and liveness probe - -Run will atomically persist owner-only process records under its state directory. Records include logical server/profile identity, private PID, start time, command fingerprint, state, exit classification, and the latest owning Job attempt/lease hash, but not raw session tokens or environment credentials. A live in-process waiter records unexpected exits. On Run startup, reconciliation probes each recorded PID, retains only matching live identities, and marks missing identities exited before new claims execute. - -Start is idempotent when a matching process is live; stop is idempotent when absent/stopped. Cancellation or timeout before start commit terminates the child. Attempt evidence prevents an older recovered assignment from replacing or stopping a newer process record. PID is intentionally private and never appears in Platform protocol results. - -OS liveness primitives provide bounded best-effort identity validation. Strong native start-token adapters for every supported OS remain future hardening; command fingerprint and journal start metadata reduce PID-reuse ambiguity in this implementation. - -### Decision 5: Secure workspace access validates every path component - -The effective workspace is `/instances//`, constructed only from validated logical identifiers. Reads and writes walk components with `Lstat`, reject symlinks, absolute/traversal/backslash keys, reserved Run state/action targets for generic writes, non-directory parents, device/FIFO/socket files, and any resolved path outside the scope. Reads require regular files and enforce a configured maximum before and during reading. - -Writes compare current file metadata with expected version/checksum, create an owner-only temporary regular file in the same verified directory, write and fsync bounded bytes, recheck containment, rename atomically, fsync the parent where supported, and atomically update an owner-only file metadata journal. A conflict performs no rename. This is preferred over `os.WriteFile`, which can follow symlinks and expose partial content. - -### Decision 6: Platform applies terminal typed results only after existing fencing - -Run returns typed execution results on the existing Job result route with Job ID, attempt, lease token, and current signed session. Platform validates type/capability consistency, bounds/redacts fields, includes the typed result in the terminal fingerprint, and stores/applies it only after the existing endpoint/session-generation/attempt/lease/deadline/cancel checks pass. - -A successful config write advances the persisted server config version and checksum and stores the approved content already attached to that Job. File reads store private bounded content on the Job but expose only checksum/size/version in the normal job projection. Lifecycle process state updates the server projection without trusting Run-supplied PID or path data. Stale/conflicting terminal results cannot mutate server config or process projection. - -### Decision 7: Frontend changes are projection-only - -`platform_web` will add schema validation for the safe execution result, show config checksum/version and process/file audit outcomes in existing server detail/job surfaces, and continue to use the current preview-then-approve flow for AI or manual config changes. It will not render approved private bytes from Job records, PID, host paths, Run tokens, leases, secret refs, sockets, or credentials, and it will reuse shared theme surfaces. - -## Risks / Trade-offs - -- [A process can exit between a liveness probe and an idempotent response] -> Record the latest observed state, keep probes bounded, and treat later status as authoritative rather than claiming continuous availability. -- [PID reuse after a long Run outage can produce ambiguous recovery on some OSes] -> Persist start metadata and command fingerprint, reject inconsistent identities, document best-effort recovery, and leave stronger per-OS birth-token adapters as follow-up hardening. -- [Platform snapshot persistence of approved config bytes increases metadata size] -> Keep input/read limits at 64 KiB and never store large artifacts in Job input; larger payloads stay artifact-referenced and are outside this change's durable artifact claim. -- [Atomic rename durability differs by filesystem] -> fsync file and parent where supported, keep same-directory temporary files, and test visibility/cleanup semantics without claiming distributed-filesystem guarantees. -- [Existing example workspaces may not contain supervised executables] -> Preserve bounded install behavior, make start failures explicit and retry policy-aware, and use controlled executable fixtures in Run tests. - -## Migration Plan - -1. Add backward-compatible zero-value execution input/result and config checksum/content fields to Platform domain/model/snapshot conversions. -2. Add protocol DTOs/validators and result projection before enabling Run capabilities. -3. Deploy Run secure workspace, process/file journals, executor, and worker dispatch; old journals load with empty new fields. -4. Update plugin action schema/examples and Platform lifecycle target resolution. -5. Enable safe frontend projections after API fields are available. -6. Rollback ignores additive snapshot fields and stops advertising new Run capabilities; supervised child processes must be stopped through the old Run instance or operator-controlled host procedure before removing its private journal. - -## Open Questions - -- Strong native process birth-token verification for every supported OS is intentionally deferred; this change uses the bounded liveness/fingerprint strategy described above. diff --git a/openspec/changes/implement-real-process-supervision-and-config-file-execution/proposal.md b/openspec/changes/implement-real-process-supervision-and-config-file-execution/proposal.md deleted file mode 100644 index 184d06b..0000000 --- a/openspec/changes/implement-real-process-supervision-and-config-file-execution/proposal.md +++ /dev/null @@ -1,31 +0,0 @@ -## Why - -Run currently executes lifecycle declarations as short-lived commands and platform config/file APIs only enqueue logical placeholders. The system therefore cannot supervise a real game process across Run restarts or prove that an approved, fenced config/file job performed a bounded workspace operation. - -## What Changes - -- Replace one-shot start/stop behavior with a restricted process supervisor that consumes plugin-declared typed action files and argument vectors, persists private process identity, reconciles surviving processes after Run restart, and provides idempotent start/stop/status outcomes without arbitrary shell execution. -- Execute `config.write`, `files.read`, and `files.write` jobs inside a profile-scoped Run workspace with lexical and filesystem containment, symlink/device rejection, bounded reads, atomic writes, version/checksum compare-and-swap, cancellation, and attempt fencing. -- Persist approved execution inputs and typed safe results on platform jobs so Platform restart does not lose a queued write body, expected version/checksum, process state, file checksum, size, or audit summary. -- Resolve lifecycle action refs and workspace scope from the server's persisted plugin runtime profile/binding while retaining owner/platform-admin/Run-service authorization and signed Run channel boundaries. -- Project successful lifecycle and config results into durable server state/config metadata and expose only safe process/config/file result fields to platform_web with existing 401/403 behavior and visual system. -- Extend plugin manifest validation and examples for typed lifecycle action contracts and add cross-repository regression coverage for process reconciliation, stale/cancelled attempts, path safety, atomic versioned file operations, ownership, signatures, and channel isolation. -- Keep durable logs/artifacts/metrics/backups, remote adapters, dependency installation, Run self-update, client-manager lifecycle, production scaling/alerts/plugin lifecycle, and real AI-provider integration outside this change. - -## Capabilities - -### New Capabilities - -- `bounded-process-supervision`: Restricted typed process start/stop/status, private durable identity, idempotency, exit observation, and Run restart reconciliation. -- `scoped-config-file-execution`: Platform-approved durable inputs and typed results for versioned, atomic, bounded config/file operations inside a contained Run workspace. - -### Modified Capabilities - - -## Impact - -- `plugins/`: lifecycle action schema/types, example declarations, manifest validation, and SDK tests; plugins still receive no Run transport or machine details. -- `platform/`: job/domain/model persistence fields, lifecycle/config/file services, authorization, Run protocol DTOs and validators, result projection, routes/contracts, and tests. -- Independent `run` repository: protocol mirrors, persistent process/file journals, secure workspace resolver, process supervisor, config/file executor, worker dispatch/recovery, and channel tests. -- `platform_web/`: safe API/job schemas and server detail status/config checksum presentation only; no unrelated redesign. -- Public API responses gain safe execution result metadata, while private approved content, PID, host paths, credentials, sessions, leases, and hashes remain outside user DTOs. diff --git a/openspec/changes/implement-real-process-supervision-and-config-file-execution/specs/bounded-process-supervision/spec.md b/openspec/changes/implement-real-process-supervision-and-config-file-execution/specs/bounded-process-supervision/spec.md deleted file mode 100644 index 2f04a05..0000000 --- a/openspec/changes/implement-real-process-supervision-and-config-file-execution/specs/bounded-process-supervision/spec.md +++ /dev/null @@ -1,68 +0,0 @@ -## ADDED Requirements - -### Requirement: Lifecycle execution uses declared typed actions -Platform and Run SHALL execute local lifecycle jobs only from the server's selected plugin runtime profile and a bounded typed action declaration whose action matches the requested capability. Run MUST reject arbitrary shell, unrestricted PATH execution, absolute executables, undeclared environment fields, and unsafe argument content. - -#### Scenario: Declared start action executes -- **WHEN** an authorized start Job carries the selected profile scope and its declared relative action ref -- **THEN** Run validates the typed start declaration and starts only the workspace-contained executable with the declared argument vector - -#### Scenario: Shell or mismatched action is rejected -- **WHEN** an action declaration contains shell execution, an unsafe executable, or an action different from the Job capability -- **THEN** Run fails the Job without creating a supervised process - -### Requirement: Process start and stop are real and idempotent -Run SHALL supervise at most one matching game process per server/profile scope and SHALL make repeated start and stop operations converge without creating duplicate processes or failing solely because the desired state already exists. - -#### Scenario: Start already-running process -- **WHEN** a start Job targets a scope whose matching supervised process is alive -- **THEN** Run returns a successful typed `running` result and does not spawn another process - -#### Scenario: Stop running process -- **WHEN** a stop Job targets a live supervised process -- **THEN** Run requests bounded graceful termination, escalates only within the declared policy, records the exit, and returns a safe `stopped` result - -#### Scenario: Stop already-stopped process -- **WHEN** a stop Job targets a scope with no live supervised process -- **THEN** Run returns an idempotent successful `stopped` result without exposing process identifiers - -### Requirement: Process state survives Run restart reconciliation -Run SHALL persist private controlled process identity/state atomically with owner-only permissions and SHALL reconcile every record against OS liveness before accepting new lifecycle work after startup or session rotation. - -#### Scenario: Live process survives Run restart -- **WHEN** Run restarts while a recorded supervised process remains alive -- **THEN** startup reconciliation retains the logical process as `running` and a later status/start operation observes the same process rather than spawning a duplicate - -#### Scenario: Process exited while Run was offline -- **WHEN** a recorded process is no longer alive during startup reconciliation -- **THEN** Run records a safe exited state and does not treat the stale PID as running - -### Requirement: Unexpected exits and status queries are typed -Run SHALL observe exits of processes it starts and SHALL return bounded typed state, exit classification, timestamps, and audit summary for status Jobs without returning PID, host path, command bytes, environment credentials, sockets, sessions, leases, or hashes. - -#### Scenario: Managed process exits unexpectedly -- **WHEN** a supervised process exits without a completed stop operation -- **THEN** Run records an unexpected-exit classification and a subsequent status result reports `exited` with bounded safe evidence - -#### Scenario: User reads process result -- **WHEN** an authorized owner or administrator reads the completed lifecycle/status Job -- **THEN** Platform returns safe process state and exit classification and omits all private machine identity and fencing fields - -### Requirement: Process operations honor cancellation and attempt fencing -Run SHALL bind process mutations to the current reconciled Job attempt and Platform SHALL apply typed terminal results only after endpoint, session generation, attempt, lease, deadline, cancellation, and signature checks succeed. - -#### Scenario: Start is cancelled before commit -- **WHEN** the current start attempt is cancelled or times out before Run commits its process record -- **THEN** Run terminates any child created by that attempt and returns a cancelled result - -#### Scenario: Stale attempt reports process result -- **WHEN** an older attempt or stale session submits a process result after retry/reconciliation -- **THEN** Platform rejects it and does not change the server process projection - -### Requirement: Process traffic remains channel-isolated -Process execution, monitoring, and result reporting SHALL use the existing Job channel and MUST NOT block control heartbeat or Job acknowledgement/result traffic when log or artifact work is blocked. - -#### Scenario: Artifact or log request blocks during process operation -- **WHEN** a log upload or artifact transfer remains blocked while a process Job completes -- **THEN** control heartbeat and the process Job acknowledgement/result continue through their independent paths - diff --git a/openspec/changes/implement-real-process-supervision-and-config-file-execution/specs/scoped-config-file-execution/spec.md b/openspec/changes/implement-real-process-supervision-and-config-file-execution/specs/scoped-config-file-execution/spec.md deleted file mode 100644 index 93d7ddb..0000000 --- a/openspec/changes/implement-real-process-supervision-and-config-file-execution/specs/scoped-config-file-execution/spec.md +++ /dev/null @@ -1,104 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform persists approved bounded execution input -Platform SHALL authorize config/file operations against server ownership, plugin permissions, endpoint ownership, selected runtime binding, declared logical target, and scoped input/artifact reference before creating a Job. Approved bounded bytes, workspace profile, expected version/checksum, and read limit SHALL persist with the Job and SHALL remain private from user Job DTOs. - -#### Scenario: Approved config survives Platform restart -- **WHEN** a reviewed config write is queued and Platform restarts before Run claims it -- **THEN** the same approved content, logical ref, expected version/checksum, and workspace scope remain available to the fenced Run assignment - -#### Scenario: Cross-owner or wrong-endpoint dispatch is attempted -- **WHEN** a caller lacks server authority or a Job/ref belongs to another server or endpoint -- **THEN** Platform rejects the request without persisting or dispatching execution input - -### Requirement: Workspace resolution prevents boundary escape -Run SHALL map the Platform-approved server ID and profile key into a private workspace and SHALL reject absolute paths, traversal, backslashes, symlinked components, symlink targets, reserved state/action targets, non-directory parents, device files, FIFOs, sockets, and any path outside the selected scope. - -#### Scenario: Traversal or absolute target is submitted -- **WHEN** a config/file Job contains a traversal, absolute, or otherwise invalid logical target -- **THEN** Platform or Run rejects it before filesystem access - -#### Scenario: Symlink escapes workspace -- **WHEN** any parent or final target is a symlink that resolves inside or outside the workspace -- **THEN** Run rejects the operation and leaves the referenced file unchanged - -#### Scenario: Device or special file is targeted -- **WHEN** a read or write resolves to a device, FIFO, socket, or other non-regular file -- **THEN** Run rejects the operation without opening the special file - -### Requirement: Config and file writes are atomic compare-and-swap operations -Run SHALL enforce bounded input, compare the current controlled version/checksum to the expected values, write an owner-only temporary regular file in the verified target directory, fsync and atomically rename it, and persist updated version/checksum metadata only after success. - -#### Scenario: Atomic write succeeds -- **WHEN** expected version/checksum match and the approved input is valid -- **THEN** readers observe either the complete old content or complete new content and Run returns the incremented version, checksum, size, and safe audit summary - -#### Scenario: Expected version conflicts -- **WHEN** the current controlled version differs from `expectedVersion` -- **THEN** Run returns a typed conflict and does not replace the file or metadata - -#### Scenario: Expected checksum conflicts -- **WHEN** the current file checksum differs from `expectedChecksum` -- **THEN** Run returns a typed conflict and leaves content/version unchanged - -#### Scenario: Write is cancelled before rename -- **WHEN** cancellation or attempt invalidation is observed before atomic commit -- **THEN** Run removes the temporary file and leaves the prior target/version unchanged - -### Requirement: Reads are bounded and typed -Run SHALL read only regular contained files, enforce the assignment's maximum before and during I/O, compute SHA-256, and return a typed result with private bounded content plus safe version/checksum/size/audit metadata. - -#### Scenario: Bounded read succeeds -- **WHEN** a contained regular file is no larger than the approved limit -- **THEN** Run returns its exact bounded content privately and reports matching checksum, size, and controlled version - -#### Scenario: File exceeds read limit -- **WHEN** file metadata or streamed bytes exceed the approved limit -- **THEN** Run fails with a bounded size error and does not return partial content - -### Requirement: Terminal config results update durable Platform state -Platform SHALL validate typed result/capability consistency after existing fencing, persist the safe result, and only then project a successful config write into the server's durable config content, checksum, version, and update time. Failed, cancelled, stale, or conflicting results MUST NOT mutate config state. - -#### Scenario: Config write result is accepted -- **WHEN** the current fenced attempt returns a successful config result matching its approved content checksum and next version -- **THEN** Platform updates the server config and authorized config reads return the new content/version/checksum - -#### Scenario: Stale config result arrives -- **WHEN** a stale attempt, invalid signature/session, wrong endpoint, or cancelled attempt returns a config result -- **THEN** Platform rejects it and preserves the prior config content/version/checksum - -### Requirement: AI suggestions remain review-before-write -AI-assisted configuration SHALL continue to produce a reviewable diff and MUST NOT dispatch a config write until an authorized user approves that diff with the current expected version/checksum. - -#### Scenario: AI suggestion is generated -- **WHEN** AI proposes configuration content -- **THEN** Platform and platform_web show a reviewable diff without creating a Run write Job - -#### Scenario: User approves suggestion -- **WHEN** an authorized user approves the current diff -- **THEN** Platform persists the approved input and dispatches it through the normal fenced config Job path - -### Requirement: User projections remain credential-free -Platform and platform_web SHALL expose only authorized process/config/file state, version, checksum, size, conflict/error classification, and bounded audit summary, and MUST NOT expose approved private Job content, raw AI keys, Run tokens, leases/hashes, secret refs, host paths, PID, sockets, or credentials. - -#### Scenario: Authorized result is rendered -- **WHEN** an authorized user views config or operation history -- **THEN** platform_web renders safe typed metadata using existing theme surfaces and existing 401/403 handling - -#### Scenario: Plugin requests a file operation -- **WHEN** a plugin page submits a declared scoped file request -- **THEN** it receives only the Platform-owned Job/safe result projection and no direct Run or workspace information - -### Requirement: Config/file traffic remains channel-isolated -Config/file execution SHALL use bounded Job payloads and MUST NOT carry log batches or artifact chunks. Slow config/file I/O MUST NOT block control heartbeat, Job acknowledgement/result, or the independent log/artifact routes. - -#### Scenario: File execution blocks -- **WHEN** a file executor is deliberately blocked -- **THEN** control heartbeat and unrelated Job acknowledgement/result requests continue within their own deadlines - -### Requirement: Roadmap boundary remains explicit -Completion of this capability MUST NOT be reported as readiness for durable logs/artifacts/metrics/backups, remote adapters, dependency installation, Run self-update, client-manager lifecycle, production scaling/alerts/plugin lifecycle, or real AI-provider integration. - -#### Scenario: Change is handed off -- **WHEN** implementation and verification complete -- **THEN** the handoff identifies those later-route capabilities as not implemented by this change diff --git a/openspec/changes/implement-real-process-supervision-and-config-file-execution/tasks.md b/openspec/changes/implement-real-process-supervision-and-config-file-execution/tasks.md deleted file mode 100644 index 1bf0174..0000000 --- a/openspec/changes/implement-real-process-supervision-and-config-file-execution/tasks.md +++ /dev/null @@ -1,33 +0,0 @@ -## 1. Contracts And Persistence - -- [x] 1.1 Extend plugin manifest schema, SDK types, examples, and validation tests for bounded typed lifecycle action declarations and refs. -- [x] 1.2 Add Platform domain/model fields for private Job execution input, typed safe/private result, and durable server config content/checksum with copy/conversion tests. -- [x] 1.3 Extend Platform and independent Run Job protocol DTOs/validators for workspace scope, expected version/checksum, bounded content/read limit, and typed results without changing lease/session fencing. -- [x] 1.4 Verify file and MySQL snapshot round trips preserve approved inputs/results/config metadata without exposing them through user Job DTOs. - -## 2. Platform Dispatch And Projection - -- [x] 2.1 Resolve lifecycle action refs and workspace scope from the persisted selected runtime profile/binding and add typed process status dispatch. -- [x] 2.2 Persist approved config/file bytes or bounded artifact payloads with expected version/checksum and enforce owner/admin, plugin, endpoint, and logical target authorization. -- [x] 2.3 Validate and persist typed Run terminal results only after existing signature/session/attempt/lease/deadline/cancel fencing. -- [x] 2.4 Project successful process/config results into durable server state/config metadata and add safe audit fields while rejecting stale, conflicting, cross-owner, and wrong-endpoint mutations. -- [x] 2.5 Update Platform routes/contracts/tests for process status, config checksum approval, bounded file inputs, safe typed Job results, and existing 401/403 behavior. - -## 3. Independent Run Execution - -- [x] 3.1 Implement a shared secure workspace resolver that rejects traversal, absolute/backslash keys, symlink components/targets, reserved writes, and special files. -- [x] 3.2 Implement an owner-only atomic file metadata journal and real bounded read/atomic CAS write executor with checksum/version conflicts and cancellation cleanup. -- [x] 3.3 Implement an owner-only atomic process journal, contained executable validation, real spawn/wait/stop/status supervision, idempotency, timeout, and unexpected-exit recording. -- [x] 3.4 Reconcile persisted process identities on Run startup/session rotation and fence stale attempts without persisting raw session tokens. -- [x] 3.5 Wire config/file/process capabilities through Worker claim/ack/progress/cancel/result/recovery and keep control/jobs independent from blocked log/artifact/file work. - -## 4. Safe Frontend Projection - -- [x] 4.1 Extend platform_web API types/schemas/tests for safe process/config/file result metadata and config checksum while rejecting forbidden machine/fencing fields. -- [x] 4.2 Render process/config/file version, checksum, size, and audit outcome in existing server detail/job surfaces using shared black-mecha/magical-girl theme primitives and existing auth error handling. - -## 5. Regression And Verification - -- [x] 5.1 Add Run regressions for idempotent start/stop, unexpected exit, restart reconciliation, stale/cancel, traversal/symlink/device escape, atomic write, CAS conflicts, bounded reads, and channel isolation. -- [x] 5.2 Add Platform regressions for durable private input, typed projection, config application, ownership/endpoint rejection, stale attempt, signature/session failure, AI review-before-approval, and channel isolation. -- [x] 5.3 Run plugin tests/typecheck/all manifest validation, Platform Go tests, platform_web tests/typecheck/build, independent Run tests, strict OpenSpec validation, structure check, shell/compose checks, and both repositories' `git diff --check`; record only passing evidence before marking complete. diff --git a/openspec/changes/implement-role-scoped-server-access/.openspec.yaml b/openspec/changes/implement-role-scoped-server-access/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-role-scoped-server-access/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-role-scoped-server-access/design.md b/openspec/changes/implement-role-scoped-server-access/design.md deleted file mode 100644 index 95e197c..0000000 --- a/openspec/changes/implement-role-scoped-server-access/design.md +++ /dev/null @@ -1,49 +0,0 @@ -## Context - -The platform already has session APIs, role-aware frontend navigation, and server lifecycle workflows. Server instances are currently global resources with no owner or administrator membership metadata, so frontend filtering alone cannot satisfy the required visibility and management rules. - -## Goals / Non-Goals - -**Goals:** -- Bootstrap the first public registration as an active platform administrator. -- Persist server ownership and server administrator membership on each server instance. -- Enforce server visibility and lifecycle authorization in platform APIs. -- Let server owners invite and remove server administrators for their own servers while hiding platform administrators from owner-facing candidate lists. -- Keep platform administrators able to view and manage all servers. - -**Non-Goals:** -- Add billing, cloud host sales, marketplace SaaS workflows, or external identity providers. -- Add persistent database migrations beyond the current in-memory model. -- Expose run credentials, raw host paths, AI provider keys, or plugin-side direct server management. - -## Decisions - -### Store ACLs on server instances - -Server instances will carry `OwnerUserID` and `AdminUserIDs` fields. This keeps authorization close to the managed resource and avoids adding a separate repository before the platform has persistent storage. - -Alternative considered: add a dedicated membership repository. This was rejected for the current in-memory platform because it increases joins and lifecycle coordination without adding durability. - -### Authorize at API/service boundaries - -Handlers will require a bearer session for user-facing server APIs and call service methods that evaluate the current user before returning data or dispatching lifecycle jobs. Platform administrators bypass the per-server ACL; server owners and server administrators require membership. - -Alternative considered: filter only in `platform_web`. This was rejected because direct API calls would still leak global server data. - -### Bootstrap first registration by existing user count - -Registration will inspect the user repository. If no users exist, the registered user becomes active with `platform-admin` and receives a session. Later registrations remain pending with `server-admin`. - -Alternative considered: keep only a seeded local admin. This was rejected because production-like setup needs a first-account bootstrap path. - -### Owner-managed administrator invitations use existing users - -Server owner invitations will accept an existing active non-platform-admin user ID and add that user to the server administrator list. Removing an administrator deletes only the server membership, not the user account or platform role. - -Alternative considered: invite by email and create accounts inline. This was rejected because the current user lifecycle already separates registration/approval from server membership assignment. - -## Risks / Trade-offs - -- [Risk] In-memory ACL state is not durable across process restart. -> Mitigation: keep this scoped to the existing in-memory platform and model fields so future persistence can mirror the contract. -- [Risk] Existing tests that create server instances without auth may fail. -> Mitigation: keep service-level direct create helpers usable while requiring auth in HTTP handlers. -- [Risk] Platform administrators accidentally appear as removable server admins. -> Mitigation: filter platform-admin users from owner-facing candidate/member responses and reject platform-admin membership mutations. diff --git a/openspec/changes/implement-role-scoped-server-access/proposal.md b/openspec/changes/implement-role-scoped-server-access/proposal.md deleted file mode 100644 index 423d0b0..0000000 --- a/openspec/changes/implement-role-scoped-server-access/proposal.md +++ /dev/null @@ -1,25 +0,0 @@ -## Why - -The current login and server management flow authenticates users but does not enforce the product rule that platform administrators can manage every server while server owners and server administrators only see their assigned servers. The platform also needs bootstrap-safe registration so the first real account becomes the platform administrator and later registrations remain server-scoped until invited or approved. - -## What Changes - -- Make the first registered account an active platform administrator with an authenticated session. -- Keep subsequent self-registrations server-scoped and pending until a platform administrator activates them. -- Add owner and administrator membership fields to server instances and expose them in bounded API responses. -- Scope server list/detail/lifecycle APIs by current user: platform administrators see all servers; owners and administrators see only owned or managed servers. -- Add server owner APIs to invite and remove server administrators without exposing platform administrators as invite candidates. -- Add frontend contracts, client methods, server detail UI, and tests for owner-managed administrator membership. - -## Capabilities - -### New Capabilities -- `role-scoped-server-access`: Registration bootstrap, server ownership, server administrator membership, and role-scoped server visibility/actions. - -### Modified Capabilities - -## Impact - -- Affects `platform/` domain, DTOs, validation, repository filters, service authorization helpers, API handlers, route docs, and tests. -- Affects `platform_web/` API types/client methods, server list/detail pages, user/admin display logic, and tests. -- Preserves existing platform/run/plugin boundaries; no raw credentials, host paths, direct sockets, or AI provider keys are exposed to plugins or the frontend. diff --git a/openspec/changes/implement-role-scoped-server-access/specs/role-scoped-server-access/spec.md b/openspec/changes/implement-role-scoped-server-access/specs/role-scoped-server-access/spec.md deleted file mode 100644 index 40c0650..0000000 --- a/openspec/changes/implement-role-scoped-server-access/specs/role-scoped-server-access/spec.md +++ /dev/null @@ -1,72 +0,0 @@ -## ADDED Requirements - -### Requirement: First registration bootstraps platform administration -The platform SHALL make the first registered user an active platform administrator and return an authenticated session for that registration. - -#### Scenario: First registered user becomes platform administrator -- **WHEN** there are no existing users and a valid registration request is submitted -- **THEN** the created user has status `active`, includes the `platform-admin` role, and receives a session token. - -#### Scenario: Later registered users remain server scoped -- **WHEN** at least one user exists and a valid registration request is submitted -- **THEN** the created user has status `pending`, includes only the `server-admin` role by default, and does not receive platform administrator privileges. - -### Requirement: Server instances carry owner and administrator membership -Server instances SHALL persist one owner user ID and zero or more server administrator user IDs. - -#### Scenario: Server is created by an authenticated server owner -- **WHEN** an authenticated non-platform user creates a server workflow -- **THEN** the created server records that user as `ownerUserId` and returns the owner in the server response. - -#### Scenario: Server membership is bounded in responses -- **WHEN** a server instance is returned by list, detail, or lifecycle APIs -- **THEN** the response includes `ownerUserId` and `adminUserIds` without exposing credentials or platform administrator-only data. - -### Requirement: Server visibility is role scoped -The platform SHALL scope user-facing server APIs by the authenticated user. - -#### Scenario: Platform administrator lists servers -- **WHEN** a platform administrator lists server instances -- **THEN** all non-filtered matching server instances are returned. - -#### Scenario: Server owner lists servers -- **WHEN** a server owner lists server instances -- **THEN** only servers where the user is the owner or a server administrator are returned. - -#### Scenario: Server administrator opens unmanaged server -- **WHEN** a server administrator requests a server they do not own or administer -- **THEN** the request is rejected with forbidden or not found semantics and no server details are returned. - -### Requirement: Server owners manage server administrators -The platform SHALL let a server owner invite and remove server administrators for servers they own. - -#### Scenario: Owner invites server administrator -- **WHEN** a server owner invites an active non-platform-admin user to administer their server -- **THEN** that user is added to the server `adminUserIds` list and can see/manage that server. - -#### Scenario: Owner removes server administrator -- **WHEN** a server owner removes an existing server administrator from their server -- **THEN** that user is removed from the server `adminUserIds` list and can no longer see that server unless they own it or have platform administrator privileges. - -#### Scenario: Owner cannot manage platform administrators -- **WHEN** a server owner lists invite candidates or attempts to add/remove a platform administrator -- **THEN** platform administrators are hidden from owner-facing membership lists and membership mutation is rejected. - -#### Scenario: Non-owner cannot change membership -- **WHEN** a server administrator attempts to invite or remove administrators for a server they do not own -- **THEN** the request is rejected. - -### Requirement: Frontend exposes owner-scoped administrator management -The frontend SHALL show server administrator management controls only where the current user can use them. - -#### Scenario: Owner sees member controls -- **WHEN** a server owner opens a server they own -- **THEN** the server detail page shows current server administrators and invitation/removal controls. - -#### Scenario: Server administrator sees no owner controls -- **WHEN** a server administrator opens a server they administer but do not own -- **THEN** the page hides invitation/removal controls while keeping allowed server operations visible. - -#### Scenario: Platform administrator can inspect all servers -- **WHEN** a platform administrator opens any server -- **THEN** the page remains accessible and avoids presenting owner-only membership controls as if the platform administrator were removable. diff --git a/openspec/changes/implement-role-scoped-server-access/tasks.md b/openspec/changes/implement-role-scoped-server-access/tasks.md deleted file mode 100644 index dccbcbc..0000000 --- a/openspec/changes/implement-role-scoped-server-access/tasks.md +++ /dev/null @@ -1,19 +0,0 @@ -## 1. Backend Model And Authorization - -- [x] 1.1 Add server owner/admin membership fields, DTOs, copy helpers, filters, and validators. -- [x] 1.2 Update registration bootstrap so the first user becomes an authenticated platform administrator. -- [x] 1.3 Add service authorization helpers for platform admin, server owner, and server administrator visibility. -- [x] 1.4 Scope server list/detail/lifecycle HTTP APIs by bearer session and server ACLs. -- [x] 1.5 Add owner APIs for listing invite candidates, inviting administrators, and removing administrators. - -## 2. Frontend User Flow - -- [x] 2.1 Add API types and client methods for server membership and invite candidates. -- [x] 2.2 Render owner/admin metadata and owner-only administrator management in server detail. -- [x] 2.3 Ensure server-only users continue landing on and seeing only their server list/detail workspaces. - -## 3. Verification - -- [x] 3.1 Add backend tests for bootstrap registration, server ACL visibility, lifecycle authorization, and owner membership changes. -- [x] 3.2 Add frontend tests for role-scoped server UI and membership client behavior. -- [x] 3.3 Run OpenSpec strict validation, structure checks, backend tests, frontend tests, and browser walkthrough. diff --git a/openspec/changes/implement-run-control-registration/.openspec.yaml b/openspec/changes/implement-run-control-registration/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-run-control-registration/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-run-control-registration/design.md b/openspec/changes/implement-run-control-registration/design.md deleted file mode 100644 index e3c98bc..0000000 --- a/openspec/changes/implement-run-control-registration/design.md +++ /dev/null @@ -1,79 +0,0 @@ -## Context - -The platform already has a `RunEndpoint` domain resource and generic create/list/detail API. The run executable currently only produces a smoke summary and a base URL-normalizing client. The architecture requires a high-priority control channel for hello, heartbeat, version, capability, and capacity metadata before later job/log/artifact channels are implemented. - -This change implements the first control-channel workflow across `run/` and `platform/`. It stays on HTTP JSON and in-memory platform storage because persistence and streaming transport are later changes. The control payloads must remain small and must not carry logs, artifact chunks, job result bodies, host paths, raw credentials, or direct sockets. - -## Goals / Non-Goals - -**Goals:** - -- Define typed run control protocol payloads in `run/protocol` and matching platform DTOs in `platform/dto`. -- Add platform control routes for hello registration and heartbeat. -- Persist/update run endpoint metadata through `service.Core` with validation and capability/capacity checks. -- Generate platform session tokens on hello and require the matching token on heartbeat. -- Add run-side client methods for hello and heartbeat. -- Add tests for protocol shape, platform service/API behavior, run client requests, and a registration/heartbeat integration flow. - -**Non-Goals:** - -- No job claim, ack, progress, result, cancel, or reconcile channel. -- No log ingest, artifact transfer, or game client bridge behavior. -- No WebSocket/gRPC streaming transport. -- No persistent database, token vault, mTLS, or auth policy engine. -- No frontend pages, plugin behavior, billing, cloud host sales, or direct plugin-to-run access. - -## Decisions - -### Decision 1: HTTP JSON control endpoints - -The initial control channel uses `POST /api/v1/run/control/hello` and `POST /api/v1/run/control/heartbeat`. This matches the existing platform API shape and keeps the first registration workflow testable without introducing a streaming dependency. - -Alternative considered: one long-lived WebSocket. Rejected because the architecture explicitly separates control from heavier job/log/artifact channels and later transport choices should be made after the metadata loop is stable. - -### Decision 2: Session token is platform-generated and in-memory - -Hello returns a deterministic session token derived from platform-side session state. Heartbeat must echo that token for the same run endpoint. The in-memory repository remains the backing state for now. - -Alternative considered: accepting a run-provided session token. Rejected because platform must own control session acceptance and future auth hardening. - -### Decision 3: Run endpoint metadata remains the platform source of truth - -Hello and heartbeat write to the existing `RunEndpoint` domain resource. This avoids a separate control-session aggregate until persistence and auth requirements need it. - -Alternative considered: adding a new run session table/model now. Rejected because current storage is in-memory and this change only needs one active session per run endpoint. - -### Decision 4: Capability fingerprint is metadata only - -Heartbeat accepts a compact capability fingerprint and may request a capability refresh when it differs from platform metadata. The full capability list is still kept on the run endpoint payload. - -Alternative considered: transmitting full capability metadata on every heartbeat. Rejected because control payloads must stay small. - -### Decision 5: Run client stays transport-only - -`run/api.PlatformClient` will encode/decode control requests and responses, but runtime scheduling, retry loops, and background heartbeat timers remain future work. - -Alternative considered: starting a daemon heartbeat loop in this change. Rejected because that would expand scope beyond registration and complicate tests before job/log/artifact channels exist. - -## Risks / Trade-offs - -- [Risk] In-memory session tokens disappear on platform restart. Mitigation: document this as early development behavior and keep token handling behind `service.Core` for future persistence. -- [Risk] Capability fingerprint refresh cannot carry detailed capability changes alone. Mitigation: heartbeat returns `refreshCapabilities` and later changes can add a capability report endpoint. -- [Risk] No auth layer means registration token validation is minimal. Mitigation: require a non-empty registration token now and leave credential verification to the auth/control hardening change. -- [Risk] Run client has methods but no daemon loop. Mitigation: keep this change testable and defer scheduling/retry policy to later run lifecycle work. - -## Migration Plan - -1. Add control protocol and DTO contracts. -2. Add platform service methods and API handlers for hello/heartbeat. -3. Add run client methods and tests for request/response behavior. -4. Update protocol/route docs. -5. Verify with platform tests, run tests, structure check, and strict OpenSpec validation. - -Rollback before dependent changes is removal of the control route/client additions and this OpenSpec change. After job/log/artifact changes depend on registered run endpoints, rollback must use a new OpenSpec change. - -## Open Questions - -- What registration credential source will replace the development registration token? -- Should session tokens become signed JWTs, opaque DB-backed tokens, or mTLS-bound session identifiers? -- What heartbeat interval and timeout thresholds should production use? diff --git a/openspec/changes/implement-run-control-registration/proposal.md b/openspec/changes/implement-run-control-registration/proposal.md deleted file mode 100644 index 0d27e2d..0000000 --- a/openspec/changes/implement-run-control-registration/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The platform can model run endpoints, but the run executor still has no real control-channel registration or heartbeat path. This change establishes the lightweight run-platform control loop so later job, log, and artifact channels can attach to known run sessions without exposing host paths or credentials. - -## What Changes - -- Add typed run control payloads for hello registration, heartbeat, capability reporting, capacity reporting, session tokens, and polling hints. -- Add platform API routes for run hello and heartbeat that create/update run endpoint metadata through `service.Core`. -- Add service-level control registration behavior that validates endpoint identity, capabilities, capacity, and session token continuity. -- Extend the run-side platform client with hello and heartbeat calls using the typed control protocol. -- Add focused platform API/service tests and run client tests, including an integration-style registration/heartbeat flow. - -## Capabilities - -### New Capabilities - -- `run-control-registration`: Platform/run control-channel registration, heartbeat, session token, capability, and capacity metadata workflow. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` and `run/` only. -- Adds Go protocol/DTO/domain/service/API code and tests for control registration. -- Updates run control documentation and platform route catalog. -- Does not implement job claim/ack/result, log ingest, artifact transfer, plugin bridge behavior, frontend pages, billing, cloud host sales, or direct plugin/run access. diff --git a/openspec/changes/implement-run-control-registration/specs/run-control-registration/spec.md b/openspec/changes/implement-run-control-registration/specs/run-control-registration/spec.md deleted file mode 100644 index 785ca7c..0000000 --- a/openspec/changes/implement-run-control-registration/specs/run-control-registration/spec.md +++ /dev/null @@ -1,71 +0,0 @@ -## ADDED Requirements - -### Requirement: Run control payloads are typed and bounded -The system SHALL define typed run control payloads for hello registration, hello response, heartbeat, heartbeat response, capability report, and capacity report without carrying logs, artifact chunks, job result bodies, host paths, raw credentials, or direct sockets. - -#### Scenario: Control payloads are used -- **WHEN** run or platform code sends control registration or heartbeat data -- **THEN** it MUST use named protocol/DTO types from dedicated protocol or DTO packages - -#### Scenario: Control payload stays lightweight -- **WHEN** run sends hello or heartbeat -- **THEN** the request MUST include run ID, display name, version, status, capability summary, and capacity metadata only - -### Requirement: Platform accepts run hello registration -The platform SHALL expose a hello endpoint that accepts a run registration request, validates it, persists or updates the run endpoint metadata, and returns a platform-generated session token with polling hints. - -#### Scenario: New run endpoint registers -- **WHEN** run sends a valid hello request for an unknown run endpoint -- **THEN** platform MUST create a run endpoint, mark it online, store capabilities/capacity, and return an accepted hello response with a session token - -#### Scenario: Existing run endpoint registers again -- **WHEN** run sends a valid hello request for an existing run endpoint -- **THEN** platform MUST update version, display name, capabilities, capacity, heartbeat time, and return a new accepted hello response - -#### Scenario: Invalid hello request is submitted -- **WHEN** run sends a missing ID, missing registration token, invalid capacity, or empty required metadata -- **THEN** platform MUST return a JSON validation error and MUST NOT create a run endpoint - -### Requirement: Platform accepts authenticated run heartbeat -The platform SHALL expose a heartbeat endpoint that requires the active platform-issued session token for the target run endpoint and updates status, capacity, heartbeat time, and capability fingerprint state. - -#### Scenario: Heartbeat succeeds -- **WHEN** run sends a heartbeat with the active session token -- **THEN** platform MUST update the run endpoint heartbeat metadata and return an accepted heartbeat response with the next heartbeat interval - -#### Scenario: Heartbeat uses invalid session token -- **WHEN** run sends a heartbeat with a missing or stale session token -- **THEN** platform MUST reject it with a JSON validation error and MUST NOT update the endpoint metadata - -#### Scenario: Capability fingerprint changes -- **WHEN** run heartbeat reports a capability fingerprint that differs from platform's known fingerprint -- **THEN** platform MUST accept the heartbeat and request capability refresh in the heartbeat response - -### Requirement: Run client performs control registration calls -The run-side platform client SHALL provide typed hello and heartbeat methods that call the platform control endpoints and decode typed responses. - -#### Scenario: Run sends hello through client -- **WHEN** run code calls the hello client method -- **THEN** the client MUST send a JSON `POST` to `/api/v1/run/control/hello` and decode the hello response - -#### Scenario: Run sends heartbeat through client -- **WHEN** run code calls the heartbeat client method -- **THEN** the client MUST send a JSON `POST` to `/api/v1/run/control/heartbeat` and decode the heartbeat response - -#### Scenario: Platform returns error -- **WHEN** the platform control endpoint returns a non-success status -- **THEN** the run client MUST return an error and MUST NOT treat the control call as accepted - -### Requirement: Control registration is documented separately from heavier channels -The run/platform route and protocol documentation SHALL identify implemented control registration routes and explicitly defer job, log, artifact, and game client bridge transport. - -#### Scenario: Contributor inspects control docs -- **WHEN** a contributor opens run or platform protocol docs -- **THEN** the docs MUST show hello/heartbeat routes as implemented and heavier channels as deferred - -### Requirement: Control registration is verified -The change SHALL include platform service/API tests, run client tests, and an integration-style hello/heartbeat flow test. - -#### Scenario: Verification commands run -- **WHEN** the change is complete -- **THEN** `go test ./...` from `platform/`, `go test ./...` from `run/`, `scripts/check-structure.sh`, and `openspec validate implement-run-control-registration --strict` MUST pass diff --git a/openspec/changes/implement-run-control-registration/tasks.md b/openspec/changes/implement-run-control-registration/tasks.md deleted file mode 100644 index 6b33489..0000000 --- a/openspec/changes/implement-run-control-registration/tasks.md +++ /dev/null @@ -1,34 +0,0 @@ -## 1. Control Contracts - -- [x] 1.1 Add typed run control protocol payloads in `run/protocol` for hello, heartbeat, capability report, and capacity report. -- [x] 1.2 Add matching platform DTO/domain contracts and conversion helpers for run control hello and heartbeat. - -## 2. Platform Control Registration - -- [x] 2.1 Extend platform service behavior to register run endpoints, issue session tokens, validate heartbeat tokens, and request capability refresh on fingerprint drift. -- [x] 2.2 Implement platform control HTTP routes for hello and heartbeat using named DTOs and service methods. -- [x] 2.3 Add platform service/API tests for new registration, re-registration, heartbeat success, invalid tokens, validation failures, and capability refresh. - -## 3. Run Control Client - -- [x] 3.1 Extend `run/api.PlatformClient` with typed hello and heartbeat methods. -- [x] 3.2 Add run client tests for request paths, JSON payloads, response decoding, and platform error handling. -- [x] 3.3 Add an integration-style test that performs platform hello then heartbeat through the run client. - -## 4. Documentation - -- [x] 4.1 Update run and platform protocol/route documentation to mark hello/heartbeat implemented and heavier channels deferred. - -## 5. Verification - -- [x] 5.1 Run `go test ./...` from `platform/` and record evidence. -- [x] 5.2 Run `go test ./...` from `run/` and record evidence. -- [x] 5.3 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.4 Run `openspec validate implement-run-control-registration --strict` and record evidence. - -## Evidence - -- 2026-07-03: `go test ./...` from `platform/` passed. -- 2026-07-03: `go test ./...` from `run/` passed. -- 2026-07-03: `scripts/check-structure.sh` passed with `structure check passed`. -- 2026-07-03: `openspec validate implement-run-control-registration --strict` passed with `Change 'implement-run-control-registration' is valid`. diff --git a/openspec/changes/implement-run-job-channel/.openspec.yaml b/openspec/changes/implement-run-job-channel/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-run-job-channel/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-run-job-channel/design.md b/openspec/changes/implement-run-job-channel/design.md deleted file mode 100644 index 2b63fae..0000000 --- a/openspec/changes/implement-run-job-channel/design.md +++ /dev/null @@ -1,78 +0,0 @@ -## Context - -The platform already stores job metadata and run endpoints, and run endpoints can register through the control channel. Existing jobs can be created with `queued` state and idempotency keys, but there is no platform/run API for a run endpoint to claim a job, acknowledge acceptance, report progress, submit a terminal result, observe cancel requests, or reconcile work after restart. - -This change implements the first job channel using HTTP JSON and in-memory platform state. It depends on registered run endpoint metadata and keeps job traffic separate from control, logs, artifacts, and the optional game client bridge. - -## Goals / Non-Goals - -**Goals:** - -- Define typed run job protocol payloads in `run/protocol` and matching platform DTO/domain contracts. -- Add platform job-channel routes for claim, ack, progress, result, cancel request, cancel polling, and reconcile. -- Add service-level leasing and lifecycle transitions for queued, accepted, running, succeeded, failed, and cancelled jobs. -- Preserve idempotency for duplicate claims, acks, and terminal results using existing job IDs and idempotency keys. -- Add run-side client methods for job-channel calls. -- Add tests for job lifecycle, invalid transitions, wrong endpoint/session behavior, idempotency, and reconciliation. - -**Non-Goals:** - -- No real process execution, plugin action execution, scheduler loop, or background run worker. -- No durable DB persistence, lease expiry sweeper, distributed locks, or multi-run fairness algorithm. -- No log ingest, artifact chunk transfer, game client bridge, frontend behavior, billing, cloud host sales, or direct plugin-to-run access. -- No raw host paths, raw credentials, direct sockets, or large result bodies in job payloads. - -## Decisions - -### Decision 1: HTTP JSON job endpoints - -The initial job channel uses small JSON `POST` endpoints under `/api/v1/run/jobs/*`. This matches the existing API style and keeps the lifecycle testable without introducing streaming transport. - -Alternative considered: long-lived WebSocket or gRPC stream for job events. Rejected for this change because the architecture separates control, jobs, logs, and artifacts, and the first job lifecycle can be proven with bounded request/response calls. - -### Decision 2: Existing job resource is the source of truth - -Job-channel operations update the existing `domain.Job` stored by the repository. Claim moves a queued job to `accepted`, ack confirms acceptance or moves to `running`, progress updates bounded progress, and result writes terminal state and a bounded result reference. - -Alternative considered: adding a separate run job lease table now. Rejected because current storage is in-memory and the existing job aggregate already contains run endpoint, state, progress, result reference, and idempotency key. - -### Decision 3: Leases are service metadata, not persisted models - -The service keeps lightweight in-memory job lease metadata keyed by job ID. Lease metadata records the run endpoint ID, session token, attempt number, lease time, last update time, cancel request flag, and terminal result fingerprint. - -Alternative considered: adding durable lease models before persistence exists. Rejected because it would create model churn without improving the current in-memory system. - -### Decision 4: Session token gates job calls - -All run job-channel calls require the active session token for the run endpoint. This reuses the control registration session and prevents stale or wrong run endpoints from mutating job state. - -Alternative considered: accepting only run endpoint ID. Rejected because hello/heartbeat already established platform-issued session continuity. - -### Decision 5: Results remain bounded references - -Terminal job result payloads carry status, message, error code, and `resultRef`. Large logs, files, backups, and config blobs must move through later log/artifact channels, not job result bodies. - -Alternative considered: allowing inline result bodies. Rejected because job result traffic must not block control, logs, or artifact transfer and must not expose raw host paths. - -## Risks / Trade-offs - -- [Risk] In-memory leases disappear on platform restart. Mitigation: keep lease handling behind `service.Core` and add reconcile behavior so a run can re-report work after reconnect. -- [Risk] No lease expiry means a stuck accepted job may remain accepted. Mitigation: expose reconciliation and cancellation now; add expiry/sweeper in a later persistence/runtime change. -- [Risk] HTTP polling has latency. Mitigation: this change prioritizes correctness and testability; later transport changes can add long-polling or streaming without changing lifecycle semantics. -- [Risk] Result references cannot prove artifact availability yet. Mitigation: keep references opaque until the artifact channel change implements checksum and transfer guarantees. - -## Migration Plan - -1. Add job protocol, DTO, domain, validation, and service contracts. -2. Add platform API handlers and tests for job lifecycle and idempotency. -3. Add run client methods and tests for request/response behavior. -4. Update protocol and route docs. -5. Verify with platform tests, run tests, structure check, and strict OpenSpec validation. - -Rollback before dependent changes is removal of the job route/client additions and this OpenSpec change. After log/artifact/server workflow changes depend on job lifecycle state, rollback must use a new OpenSpec change. - -## Open Questions - -- What production lease duration and retry policy should run endpoints use? -- Should queued job claim ordering later support priority, FIFO only, or per-server concurrency limits? -- Should terminal result fingerprints be signed, checksummed, or backed by artifact metadata once artifact transfer exists? diff --git a/openspec/changes/implement-run-job-channel/proposal.md b/openspec/changes/implement-run-job-channel/proposal.md deleted file mode 100644 index c8a4ff0..0000000 --- a/openspec/changes/implement-run-job-channel/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -Run endpoints can register and heartbeat, but they still cannot receive bounded platform jobs or report lifecycle state. This change adds the job channel needed for server lifecycle and plugin-triggered work while keeping it independent from control, log ingest, artifact transfer, and game client bridge traffic. - -## What Changes - -- Add typed run job protocol payloads for claim, ack, progress, result, cancel, and reconcile workflows. -- Add platform API routes that let registered run endpoints claim queued jobs, acknowledge acceptance, report progress, submit terminal results, fetch cancel requests, and reconcile active work after restart. -- Extend platform service behavior for job leasing, idempotent claims/acks/results, lifecycle validation, and cancellation metadata using the existing in-memory repository. -- Extend the run-side platform client with typed job-channel methods. -- Add focused platform service/API tests and run client tests, including job lifecycle and idempotency/reconciliation coverage. - -## Capabilities - -### New Capabilities - -- `run-job-channel`: Platform/run job-channel lifecycle, lease, acknowledgement, progress, result, cancel, reconcile, and idempotency workflow. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` and `run/` only. -- Adds Go protocol/DTO/domain/service/API code and tests for the job channel. -- Updates run/platform protocol and route documentation. -- Does not implement durable log ingest, artifact chunk transfer, game client bridge behavior, frontend pages, billing, cloud host sales, or direct plugin-to-run/plugin-to-platform bypasses. diff --git a/openspec/changes/implement-run-job-channel/specs/run-job-channel/spec.md b/openspec/changes/implement-run-job-channel/specs/run-job-channel/spec.md deleted file mode 100644 index fea3c76..0000000 --- a/openspec/changes/implement-run-job-channel/specs/run-job-channel/spec.md +++ /dev/null @@ -1,104 +0,0 @@ -## ADDED Requirements - -### Requirement: Run job payloads are typed and bounded -The system SHALL define typed run job payloads for claim, claim response, ack, progress, result, cancel request, cancel polling, and reconcile workflows without carrying logs, artifact chunks, host paths, raw credentials, direct sockets, or large inline result bodies. - -#### Scenario: Job payloads are used -- **WHEN** run or platform code sends job lifecycle data -- **THEN** it MUST use named protocol/DTO types from dedicated protocol or DTO packages - -#### Scenario: Job payload stays bounded -- **WHEN** run submits job progress or result -- **THEN** the request MUST include job identity, run endpoint identity, session token, lifecycle state, progress metadata, message, error code, and result reference only - -### Requirement: Platform lets run claim queued jobs -The platform SHALL expose a job claim endpoint that validates run session continuity, selects a queued job assigned to the run endpoint, leases it, and returns bounded job metadata. - -#### Scenario: Run claims queued job -- **WHEN** a registered run endpoint requests a job claim and a queued job exists for that endpoint -- **THEN** platform MUST mark the job accepted, return the job metadata, lease token, attempt number, and polling hints - -#### Scenario: No queued job exists -- **WHEN** a registered run endpoint requests a job claim and no queued job exists for that endpoint -- **THEN** platform MUST return an accepted empty claim response without changing unrelated jobs - -#### Scenario: Claim uses invalid session token -- **WHEN** run submits a claim with a missing or stale session token -- **THEN** platform MUST return a JSON validation error and MUST NOT change job state - -### Requirement: Platform accepts job acknowledgements and progress -The platform SHALL expose job ack and progress endpoints that require the active session token and active job lease for the run endpoint. - -#### Scenario: Job ack succeeds -- **WHEN** run acknowledges an active lease for an accepted job -- **THEN** platform MUST keep or move the job to a running lifecycle state and return an accepted ack response - -#### Scenario: Job progress succeeds -- **WHEN** run reports bounded progress for an accepted or running job -- **THEN** platform MUST update percent, message, heartbeat time, and return an accepted progress response - -#### Scenario: Invalid progress is submitted -- **WHEN** run reports progress outside 0 through 100 or with a stale lease token -- **THEN** platform MUST return a JSON validation error and MUST NOT update the job - -### Requirement: Platform accepts idempotent terminal job results -The platform SHALL expose a result endpoint that accepts terminal succeeded, failed, or cancelled results for an active lease and treats repeated equivalent terminal result submissions as idempotent. - -#### Scenario: Job result succeeds -- **WHEN** run submits a valid terminal result for an active lease -- **THEN** platform MUST update the job terminal state, progress, result reference, and return an accepted result response - -#### Scenario: Duplicate terminal result is submitted -- **WHEN** run repeats the same terminal result for a job already in that terminal state -- **THEN** platform MUST return the same accepted terminal result response without mutating unrelated metadata - -#### Scenario: Conflicting terminal result is submitted -- **WHEN** run submits a different terminal result for a job already terminal -- **THEN** platform MUST return a JSON validation error and MUST NOT overwrite the existing result - -### Requirement: Platform supports job cancellation polling -The platform SHALL expose a service/API path to request cancellation for a job and a run-facing path to poll cancellation for the active lease. - -#### Scenario: Platform requests cancellation -- **WHEN** platform requests cancellation for an accepted or running job -- **THEN** platform MUST record the cancel request and keep the job available for run cancellation polling - -#### Scenario: Run polls cancellation -- **WHEN** run polls cancellation for an active leased job with a cancel request -- **THEN** platform MUST return a cancel response naming that job and cancellation reason - -### Requirement: Platform supports run reconciliation -The platform SHALL expose a reconcile endpoint that lets a registered run endpoint report active job IDs after restart and receive platform-known active jobs for that endpoint. - -#### Scenario: Run reconciles active jobs -- **WHEN** run submits active job IDs for its endpoint after restart -- **THEN** platform MUST return active jobs known to the platform for that endpoint and mark unknown reported jobs for run-side cleanup - -#### Scenario: Reconcile uses invalid session token -- **WHEN** run submits reconcile with a missing or stale session token -- **THEN** platform MUST return a JSON validation error and MUST NOT change job state - -### Requirement: Run client performs job-channel calls -The run-side platform client SHALL provide typed claim, ack, progress, result, cancel polling, and reconcile methods that call the platform job endpoints and decode typed responses. - -#### Scenario: Run sends job channel calls through client -- **WHEN** run code calls job-channel client methods -- **THEN** the client MUST send JSON `POST` requests to the matching `/api/v1/run/jobs/*` endpoints and decode typed responses - -#### Scenario: Platform returns job error -- **WHEN** a platform job endpoint returns a non-success status -- **THEN** the run client MUST return an error and MUST NOT treat the job call as accepted - -### Requirement: Job channel is documented separately from other channels -The run/platform route and protocol documentation SHALL identify implemented job-channel routes and explicitly keep control, log ingest, artifact transfer, and game client bridge transport separate. - -#### Scenario: Contributor inspects job docs -- **WHEN** a contributor opens run or platform protocol docs -- **THEN** the docs MUST show job claim, ack, progress, result, cancel polling, and reconcile routes as implemented while heavier log/artifact channels remain deferred - -### Requirement: Job channel is verified -The change SHALL include platform service/API tests, run client tests, job lifecycle tests, and idempotency/reconciliation tests. - -#### Scenario: Verification commands run -- **WHEN** the change is complete -- **THEN** `go test ./...` from `platform/`, `go test ./...` from `run/`, `scripts/check-structure.sh`, and `openspec validate implement-run-job-channel --strict` MUST pass diff --git a/openspec/changes/implement-run-job-channel/tasks.md b/openspec/changes/implement-run-job-channel/tasks.md deleted file mode 100644 index 4a1337f..0000000 --- a/openspec/changes/implement-run-job-channel/tasks.md +++ /dev/null @@ -1,35 +0,0 @@ -## 1. Job Contracts - -- [x] 1.1 Add typed run job protocol payloads in `run/protocol` for claim, ack, progress, result, cancel polling, and reconcile. -- [x] 1.2 Add matching platform DTO/domain contracts and conversion helpers for job-channel requests and responses. -- [x] 1.3 Add validation rules for bounded job payloads, lifecycle states, progress, session, lease, and terminal result metadata. - -## 2. Platform Job Channel - -- [x] 2.1 Extend platform service behavior for job claim leasing, session checks, ack, progress, terminal results, cancellation, reconcile, and idempotency. -- [x] 2.2 Implement platform job-channel HTTP routes using named DTOs and service methods. -- [x] 2.3 Add platform service/API tests for lifecycle success, no-job claim, invalid session/lease, invalid progress, cancellation, duplicate results, conflicting results, and reconcile. - -## 3. Run Job Client - -- [x] 3.1 Extend `run/api.PlatformClient` with typed job claim, ack, progress, result, cancel polling, and reconcile methods. -- [x] 3.2 Add run client tests for request paths, JSON payloads, response decoding, and platform error handling. -- [x] 3.3 Add an integration-style client test that claims a job, acknowledges it, reports progress, submits a result, and reconciles against a test platform job endpoint. - -## 4. Documentation - -- [x] 4.1 Update run and platform protocol/route documentation to mark job-channel endpoints implemented and keep log/artifact/game-client channels separate. - -## 5. Verification - -- [x] 5.1 Run `go test ./...` from `platform/` and record evidence. -- [x] 5.2 Run `go test ./...` from `run/` and record evidence. -- [x] 5.3 Run `scripts/check-structure.sh` and record evidence. -- [x] 5.4 Run `openspec validate implement-run-job-channel --strict` and record evidence. - -## Evidence - -- 2026-07-03: `go test ./...` from `platform/` passed. -- 2026-07-03: `go test ./...` from `run/` passed. -- 2026-07-03: `scripts/check-structure.sh` passed with `structure check passed`. -- 2026-07-03: `openspec validate implement-run-job-channel --strict` passed with `Change 'implement-run-job-channel' is valid`. diff --git a/openspec/changes/implement-run-worker-real-execution/.openspec.yaml b/openspec/changes/implement-run-worker-real-execution/.openspec.yaml deleted file mode 100644 index dd9a1d9..0000000 --- a/openspec/changes/implement-run-worker-real-execution/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-06 diff --git a/openspec/changes/implement-run-worker-real-execution/design.md b/openspec/changes/implement-run-worker-real-execution/design.md deleted file mode 100644 index dce8539..0000000 --- a/openspec/changes/implement-run-worker-real-execution/design.md +++ /dev/null @@ -1,58 +0,0 @@ -## Context - -Run has typed clients for control/job/log/artifact channels and local spool packages, but its executable behavior is still a smoke summary plus bounded lifecycle executor that immediately returns success metadata. Platform-side job leasing is already available, so the missing piece is a persistent run worker that consumes jobs safely. - -## Goals / Non-Goals - -**Goals:** - -- Add hello/heartbeat and job polling loops. -- Execute install/start/stop lifecycle jobs using scoped process supervision. -- Emit progress and terminal results through the job channel. -- Connect stdout/stderr to log spool and lifecycle artifacts to artifact queue hooks. -- Enforce path, credential, command, and socket safety. - -**Non-Goals:** - -- No arbitrary plugin code execution or unbounded shell access. -- No game client bridge implementation. -- No cloud host provisioning or billing. -- No external artifact/log storage backend implementation. - -## Decisions - -### Decision 1: Worker owns channel scheduling - -The run worker keeps control heartbeat high priority, job claim/result next, logs durable/batched, and artifacts lower priority. Long transfers must not block heartbeat or job result submission. - -### Decision 2: Lifecycle actions use scoped command templates - -Plugin lifecycle action references resolve to bounded command templates under a configured server workspace. Absolute paths, parent traversal, raw credentials, and socket exposure are rejected. - -### Decision 3: Process supervisor is an abstraction - -Process management sits behind a supervisor interface so tests can use fake processes and later game-specific process handling can be added without rewriting the worker loop. - -### Decision 4: Smoke mode remains - -Smoke mode stays available for local diagnostics. Worker mode is enabled through explicit config. - -## Risks / Trade-offs - -- [Risk] Real process orchestration can hang. Mitigation: bounded timeouts, cancellation, progress heartbeat, and supervisor tests. -- [Risk] Command templates can become unsafe. Mitigation: validation rejects shell metacharacter abuse, absolute paths, direct sockets, and secret env leaks. -- [Risk] Worker loops can starve logs/artifacts. Mitigation: separate scheduling and priority rules. - -## Migration Plan - -1. Add worker config and session state. -2. Implement control heartbeat and job loop. -3. Add process supervisor and lifecycle executor. -4. Wire logs/artifacts to existing queues. -5. Update command entrypoint and docs. -6. Add unit and integration-style tests. - -## Open Questions - -- Whether future plugin action runtimes should interpret JSON action schemas directly or compile them into lifecycle command templates. -- Whether server process state should be persisted in a journal file or a small local database. diff --git a/openspec/changes/implement-run-worker-real-execution/proposal.md b/openspec/changes/implement-run-worker-real-execution/proposal.md deleted file mode 100644 index 9acf610..0000000 --- a/openspec/changes/implement-run-worker-real-execution/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The run executor currently returns metadata-only success for lifecycle assignments. The platform can queue and claim jobs, but no daemon loop performs hello, heartbeat, claim, ack, progress, result, cancel, reconcile, process supervision, log collection, or artifact worker coordination. Operators need real local execution before server management can be considered operational. - -## What Changes - -- Add a run worker loop for registration, heartbeat, job polling, acknowledgement, progress, results, cancel polling, and reconcile. -- Replace metadata-only lifecycle execution with scoped install/start/stop process orchestration. -- Enforce workspace scoping, command allowlists, redaction, and channel separation. -- Connect process output to log spool and lifecycle result refs to artifact upload hooks. -- Add configuration, tests, and integration-style verification with a platform test server. - -## Capabilities - -### New Capabilities - -- `run-worker-real-execution`: Real run-side worker loop and scoped lifecycle process execution. - -### Modified Capabilities - -- `server-management-workflows`: Lifecycle jobs become executable by run instead of metadata-only. -- `run-job-channel`: The run client is used by a persistent worker loop. - -## Impact - -- Affects `run/` config, command, runtime, protocol usage, spool integration, artifact hooks, docs, and tests. -- Affects `platform/` tests where integration-style job flow coverage is needed. -- Does not expose host paths, raw credentials, direct sockets, unrestricted shell execution, billing, or cloud host workflows. diff --git a/openspec/changes/implement-run-worker-real-execution/specs/run-worker-real-execution/spec.md b/openspec/changes/implement-run-worker-real-execution/specs/run-worker-real-execution/spec.md deleted file mode 100644 index 9026e16..0000000 --- a/openspec/changes/implement-run-worker-real-execution/specs/run-worker-real-execution/spec.md +++ /dev/null @@ -1,45 +0,0 @@ -## ADDED Requirements - -### Requirement: Run worker maintains platform session -The run executable SHALL support a worker mode that registers with platform and maintains heartbeat state. - -#### Scenario: Worker registers and heartbeats -- **WHEN** run starts in worker mode with valid platform configuration -- **THEN** it MUST send hello, store the active session token, and continue sending heartbeat metadata - -#### Scenario: Heartbeat does not carry heavy channels -- **WHEN** run sends heartbeat -- **THEN** it MUST NOT include logs, artifact chunks, job result bodies, host paths, raw credentials, or direct sockets - -### Requirement: Run worker processes job lifecycle -The run worker SHALL claim, acknowledge, report progress, complete, cancel, and reconcile jobs through the platform job channel. - -#### Scenario: Job assignment completes -- **WHEN** platform assigns a supported lifecycle job -- **THEN** run MUST ack the job, report bounded progress, execute scoped lifecycle work, and submit a terminal result - -#### Scenario: Cancel request handled -- **WHEN** platform reports cancellation for an active job lease -- **THEN** run MUST attempt cancellation and submit a bounded cancelled or failed result - -### Requirement: Lifecycle execution is scoped -The run worker SHALL execute install, start, and stop lifecycle commands only inside configured server workspaces with validated command templates. - -#### Scenario: Scoped lifecycle command accepted -- **WHEN** a lifecycle job resolves to a safe command template and workspace -- **THEN** run MUST execute it through the process supervisor and redact unsafe output before platform reporting - -#### Scenario: Unsafe lifecycle command rejected -- **WHEN** a lifecycle job requests absolute paths, parent traversal, raw credentials, direct sockets, or unrestricted shell execution -- **THEN** run MUST reject the job with a bounded failure result - -### Requirement: Run channels remain prioritized -The run worker SHALL keep control, job, log, and artifact work channelized so large transfer work cannot block heartbeat or job result submission. - -#### Scenario: Artifact work pending during heartbeat -- **WHEN** artifact uploads are pending and a heartbeat is due -- **THEN** run MUST prioritize heartbeat over artifact transfer work - -#### Scenario: Process logs are spooled -- **WHEN** a managed process writes stdout or stderr -- **THEN** run MUST write bounded log entries to local spool for platform ingest diff --git a/openspec/changes/implement-run-worker-real-execution/tasks.md b/openspec/changes/implement-run-worker-real-execution/tasks.md deleted file mode 100644 index 2dd397a..0000000 --- a/openspec/changes/implement-run-worker-real-execution/tasks.md +++ /dev/null @@ -1,56 +0,0 @@ -## 1. Run Worker Loop - -- [x] 1.1 Add run worker service that performs hello registration and stores active session state. -- [x] 1.2 Add heartbeat loop with capability refresh and capacity reporting. -- [x] 1.3 Add job claim loop with ack, progress, result, cancel polling, and reconcile. -- [x] 1.4 Add bounded retry/backoff behavior without blocking heartbeat. - -## 2. Process Lifecycle Execution - -- [x] 2.1 Replace metadata-only lifecycle executor with scoped install/start/stop execution. -- [x] 2.2 Add process supervisor abstraction for server working directory, command templates, env allowlist, and lifecycle state. -- [x] 2.3 Add safe command resolution from plugin lifecycle action schemas without unrestricted shell execution. -- [x] 2.4 Add local state/journal for active server processes and in-flight jobs. -- [x] 2.5 Add cancellation behavior for running lifecycle jobs. - -## 3. Security Boundaries - -- [x] 3.1 Enforce scoped server workspace roots and never expose raw host paths to platform_web or plugins. -- [x] 3.2 Reject plugin action payloads requesting raw credentials, direct sockets, absolute paths, or unrestricted commands. -- [x] 3.3 Redact command output and metadata before sending progress/result. -- [x] 3.4 Keep control, job, log, and artifact channels independent. - -## 4. Log And Artifact Worker Hooks - -- [x] 4.1 Connect process stdout/stderr to the existing log spool. -- [x] 4.2 Add artifact upload hook for lifecycle result refs. -- [x] 4.3 Ensure large artifact work cannot block control heartbeat or job result submission. - -## 5. CLI And Config - -- [x] 5.1 Add run config for platform URL, run endpoint ID, registration token, workspace root, poll intervals, and capacity. -- [x] 5.2 Update `run/cmd/run` to start the worker in local mode. -- [x] 5.3 Keep smoke mode available for tests and local diagnostics. - -## 6. Verification - -- [x] 6.1 Add unit tests for worker state transitions, retry behavior, and cancel/reconcile. -- [x] 6.2 Add run tests for scoped lifecycle command execution using temp workspaces. -- [x] 6.3 Add integration-style test with a platform test server: hello → heartbeat → claim → ack → progress → result. -- [x] 6.4 Run `cd run && go test ./...` and record evidence. -- [x] 6.5 Run `cd platform && go test ./...` and record evidence. -- [x] 6.6 Run `scripts/check-structure.sh` and record evidence. -- [x] 6.7 Run `openspec validate implement-run-worker-real-execution --strict` and record evidence. - -## Evidence - -- 2026-07-06: Added `run/runtime.Worker` with hello session registration, heartbeat, claim, ack, progress, cancel polling, terminal result, reconcile, bounded retry ticker reset, and an in-memory active job journal. -- 2026-07-06: Replaced metadata-only lifecycle execution with scoped command-template execution through `ProcessSupervisor`, per-server workspace resolution, command/env validation, cancellation, redaction, log sink, and lifecycle artifact hook. -- 2026-07-06: Added `run/config` worker settings for endpoint identity, registration token, workspace/spool roots, max jobs, heartbeat/poll intervals, and retry backoff; updated `run/cmd/run` to preserve smoke mode and start worker mode when `RUN_MODE=worker`. -- 2026-07-06: Updated `run/README.md`, `run/protocol/job.md`, and `run/protocol/control.md` to document real worker mode, scoped lifecycle command templates, and channel boundaries. -- 2026-07-06: `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./runtime` passed after adding lifecycle tests for scoped command execution, unsafe template rejection, workspace escape rejection, cancellation, log sink, artifact hook, worker registration, heartbeat, claim/ack/progress/result, cancel/reconcile, spool token propagation, bounded retry, and HTTP platform-like worker flow. -- 2026-07-06: Updated `platform/api/job_channel_handlers_test.go` so the platform router workflow covers `hello -> heartbeat -> claim -> ack -> progress -> cancel -> result -> reconcile`. -- 2026-07-06: `cd run && GOCACHE=/private/tmp/browser-go-build-cache go test ./...` passed with escalated loopback permission because existing API/worker `httptest` suites bind local ports. -- 2026-07-06: `cd platform && GOCACHE=/private/tmp/browser-go-build-cache go test ./...` passed. -- 2026-07-06: `scripts/check-structure.sh` passed. -- 2026-07-06: `openspec validate implement-run-worker-real-execution --strict` reported `Change 'implement-run-worker-real-execution' is valid`; PostHog telemetry flush failed due restricted DNS and did not affect validation. diff --git a/openspec/changes/implement-scum-server-plugin/design.md b/openspec/changes/implement-scum-server-plugin/design.md deleted file mode 100644 index 19cba44..0000000 --- a/openspec/changes/implement-scum-server-plugin/design.md +++ /dev/null @@ -1,38 +0,0 @@ -## Context - -The platform already supports registry-backed game plugins, marketplace projection, server lifecycle workflows, run-side lifecycle jobs, and browser-visible server controls. The missing piece is a concrete SCUM plugin package that follows those contracts instead of relying on the generic `game.example` development fixture. - -## Goals / Non-Goals - -**Goals:** - -- Provide a SCUM plugin directory with manifest, create form schema, lifecycle action templates, and platform-hosted page declarations. -- Keep lifecycle requests platform-mediated and safe: plugin metadata must expose only logical action refs, capabilities, page keys, and permissions. -- Prove one SCUM plugin can be registered, discovered in the marketplace, and used to create at least two SCUM server instances. -- Preserve the existing platform_web management console style and reuse the generic server/plugin surfaces. - -**Non-Goals:** - -- No real commercial SCUM binary download or production hosting orchestration. -- No direct browser/plugin connection to run endpoints. -- No cloud-provider, host-sales, billing, or unrelated marketplace behavior. - -## Decisions - -### Decision 1: SCUM plugin is a first-party local example plugin - -The plugin lives under `plugins/examples/scum-server-plugin` and follows the same manifest and validation schema as other game management plugins. - -### Decision 2: Lifecycle templates use safe local fixture commands - -The action templates use bounded executable names and arguments accepted by the run lifecycle executor. They prove install/start/stop wiring without exposing real host paths or requiring a SCUM dedicated server binary. - -### Decision 3: Platform and frontend reuse existing plugin contracts - -No new platform_web route or visual system is introduced. The SCUM plugin declares overview/config/log pages, and the existing marketplace/server management/plugin bridge surfaces render those declarations with the current theme-aware console components. - -## Risks / Mitigations - -- [Risk] The SCUM proof could be mistaken for production SCUM hosting. Mitigation: describe it as a local fixture plugin and keep action templates bounded. -- [Risk] Adding another plugin could drift from manifest safety rules. Mitigation: default validation now checks both example and SCUM manifests. -- [Risk] Browser-facing surfaces could expose unsafe details. Mitigation: local debug smoke rejects forbidden fragments from registration, marketplace, server, job, log, and artifact responses. diff --git a/openspec/changes/implement-scum-server-plugin/proposal.md b/openspec/changes/implement-scum-server-plugin/proposal.md deleted file mode 100644 index a9099c6..0000000 --- a/openspec/changes/implement-scum-server-plugin/proposal.md +++ /dev/null @@ -1,22 +0,0 @@ -## Why - -The current lifecycle proof uses the generic development plugin, but operators need a concrete SCUM server plugin that can be discovered from the plugin marketplace and used to create multiple SCUM server instances through the platform-mediated lifecycle path. - -## What Changes - -- Add a first-party local SCUM server plugin directory under `plugins/examples/scum-server-plugin`. -- Copy the existing safe plugin lifecycle pattern into SCUM-specific manifest metadata, create-form schema, plugin pages, and lifecycle action templates. -- Update plugin validation so both the development plugin and SCUM plugin are validated by default. -- Extend local debug smoke coverage so the plugin marketplace can discover the SCUM plugin and the platform can create multiple SCUM server instances from it. - -## Capabilities - -### New Capabilities - -- `scum-server-plugin`: Provides a local SCUM game management plugin discoverable through the plugin marketplace and usable for multi-instance platform-mediated lifecycle workflows. - -## Impact - -- Affects `plugins/` example plugin assets, manifest validation, and tests. -- Affects `scripts/local-debug-smoke.sh` registration and API proof data. -- Does not add billing, cloud host sales, unrelated SaaS marketplace behavior, direct plugin-run transport, raw credentials, direct sockets, or raw host paths. diff --git a/openspec/changes/implement-scum-server-plugin/specs/scum-server-plugin/spec.md b/openspec/changes/implement-scum-server-plugin/specs/scum-server-plugin/spec.md deleted file mode 100644 index df40e5e..0000000 --- a/openspec/changes/implement-scum-server-plugin/specs/scum-server-plugin/spec.md +++ /dev/null @@ -1,22 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM plugin package -The repository SHALL include a first-party local SCUM server plugin package with manifest metadata, create-form schema, lifecycle action templates, and plugin page declarations. - -#### Scenario: SCUM manifest validates -- **WHEN** plugin manifest validation runs with default targets -- **THEN** it MUST validate the SCUM plugin manifest and reject unsafe raw host paths, direct run sockets, bearer credentials, passwords, raw AI keys, and undeclared transport details - -### Requirement: SCUM plugin marketplace discovery -The platform SHALL be able to register the SCUM plugin manifest and project it into the plugin marketplace using safe platform registry metadata. - -#### Scenario: SCUM plugin is discoverable -- **WHEN** the SCUM plugin manifest is registered through platform APIs -- **THEN** `GET /api/v1/plugin-marketplace/plugins?serverType=scum&keyword=scum` MUST include the SCUM plugin without exposing unsafe fields - -### Requirement: SCUM multi-instance creation -The platform SHALL allow the SCUM plugin to create multiple independent SCUM server instances through the existing platform-mediated server lifecycle workflow. - -#### Scenario: Two SCUM servers are created from one plugin -- **WHEN** local debug proof creates two server instances using the SCUM plugin -- **THEN** both instances MUST have distinct IDs and names, share the SCUM plugin association, and receive independent lifecycle install jobs diff --git a/openspec/changes/implement-scum-server-plugin/tasks.md b/openspec/changes/implement-scum-server-plugin/tasks.md deleted file mode 100644 index 6664b65..0000000 --- a/openspec/changes/implement-scum-server-plugin/tasks.md +++ /dev/null @@ -1,50 +0,0 @@ -## 1. SCUM Plugin Assets - -- [x] 1.1 Add `plugins/examples/scum-server-plugin` with SCUM manifest metadata, create-form schema, pages, and lifecycle action templates. -- [x] 1.2 Ensure plugin manifest validation accepts the SCUM plugin and still rejects unsafe transport/credential/path content. - -## 2. Discovery and Multi-Instance Proof - -- [x] 2.1 Update local debug smoke registration to register SCUM plugin metadata through platform APIs. -- [x] 2.2 Update local debug smoke creation proof to create two SCUM servers from the SCUM plugin and verify marketplace discovery. - -## 3. Verification - -- [x] 3.1 Run `cd plugins && npm run typecheck && npm run test && npm run validate:manifest`. -- [x] 3.2 Run `scripts/check-structure.sh`. -- [x] 3.3 Run `openspec validate implement-scum-server-plugin --strict`. -- [x] 3.4 Run focused platform/platform_web checks and a browser walkthrough if UI behavior changes beyond existing generic surfaces. - -## Evidence - -- Plugin validation: - - `cd plugins && npm run typecheck` passed. - - `cd plugins && npm run test` passed: `tests/manifest-validation.test.ts` passed 12 tests, including SCUM manifest validation. - - Initial sandbox `cd plugins && npm run validate:manifest` failed because `tsx` could not create its local IPC pipe (`EPERM`); escalated rerun passed. - - `cd plugins && npm run validate:manifest` validated both `examples/dev-game-plugin/manifest.json` and `examples/scum-server-plugin/manifest.json`. - -- SCUM plugin implementation: - - Added `plugins/examples/scum-server-plugin/manifest.json` with `game.scum`, server type `scum`, SCUM-specific marketplace metadata, lifecycle actions, bridge actions, permissions, plugin pages, and AI purposes. - - Added `plugins/examples/scum-server-plugin/schemas/create-form.schema.json` with SCUM server name, game port, query port, and max-player fields. - - Added scoped lifecycle fixtures under `plugins/examples/scum-server-plugin/actions/` for install/start/stop/restart/status using safe bounded command templates. - - Updated `plugins/package.json`, `plugins/tests/manifest-validation.test.ts`, and `scripts/check-structure.sh` so SCUM plugin assets are part of default validation. - -- API/local debug proof: - - Updated `scripts/local-debug-smoke.sh` to register `game.scum` through `/api/v1/game-plugins/register-manifest`. - - Updated smoke proof to create `scum-alpha` and `scum-beta` through `/api/v1/server-instances/workflows/create`. - - Initial smoke against `18080` failed because that port was already occupied by stale state. Isolated rerun on `18087` with `LOCAL_DEBUG_ROOT=/private/tmp/browser-scum-local-debug-proof-2` passed. - - Passing smoke evidence directory: `/private/tmp/browser-scum-local-debug-proof-2/smoke`. - - Smoke verified `/api/v1/plugin-marketplace/plugins?serverType=scum&keyword=scum` contains `game.scum`, and both SCUM instances have separate lifecycle install job records. - -- Browser walkthrough: - - Started an isolated browser verification stack on platform `127.0.0.1:18088` and frontend `127.0.0.1:5188`. - - Seeded the stack with `LOCAL_DEBUG_SELF_START=false LOCAL_DEBUG_PLATFORM_PORT=18088 LOCAL_DEBUG_WEB_PORT=5188 LOCAL_DEBUG_ROOT=/private/tmp/browser-scum-browser-proof scripts/local-debug-smoke.sh`; smoke passed. - - Logged into `http://127.0.0.1:5188/` as `operator.local@example.test / operator-local`; 首页 showed API-backed platform data and `game.scum: 2 个实例`. - - 插件市场 showed `SCUM Server`, `game.scum`, status `已安装`, server type `scum`, and capabilities `process.install`, `process.start`, `process.stop`; no local fallback or forbidden fragments were visible. - - 服务器管理 showed `SCUM Alpha` (`scum-alpha`) and `SCUM Beta` (`scum-beta`) as separate server cards; no local fallback or forbidden fragments were visible. - - Opened `#/servers/scum-alpha`, confirmed plugin binding `game.scum@0.1.0`, started the server via the visible `启动` control, confirmed the dialog, and observed `SCUM Alpha` change to `运行中` / `在线` with `停止` enabled. - -- Final gates: - - `bash -n scripts/local-debug-smoke.sh` passed. - - `scripts/check-structure.sh` passed with `structure check passed`. - - `openspec validate implement-scum-server-plugin --strict` passed with `Change 'implement-scum-server-plugin' is valid`; PostHog telemetry flush reported `ENOTFOUND edge.openspec.dev`, which did not affect validation. diff --git a/openspec/changes/implement-secure-client-manager-lifecycle/.openspec.yaml b/openspec/changes/implement-secure-client-manager-lifecycle/.openspec.yaml deleted file mode 100644 index ff5f854..0000000 --- a/openspec/changes/implement-secure-client-manager-lifecycle/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-17 diff --git a/openspec/changes/implement-secure-client-manager-lifecycle/design.md b/openspec/changes/implement-secure-client-manager-lifecycle/design.md deleted file mode 100644 index 1944f7e..0000000 --- a/openspec/changes/implement-secure-client-manager-lifecycle/design.md +++ /dev/null @@ -1,107 +0,0 @@ -## Context - -The existing distribution workflow validates a plugin-declared client-manager profile, queues a real `distribution.build` job, injects a separate component key through authenticated build input, and publishes an available artifact only after chunked upload succeeds. It does not represent an installed instance, deploy the artifact through Run, supervise the companion process, authenticate the component as its own actor, reconcile health after Platform or Run restarts, or provide update/rollback/uninstall workflows. - -The implementation spans the plugin contract, Platform persistence and APIs, the independent Run repository, and platform_web. Existing boundaries remain mandatory: Run is not reintroduced into this repository; browser/plugin callers never receive raw secrets, paths, PIDs, sockets, or endpoint addresses; job, artifact, log, control, and optional game-client traffic remain isolated; and existing dirty changes in both repositories must be preserved. - -## Goals / Non-Goals - -**Goals:** - -- Carry a plugin-declared client manager from a real build artifact through authorized deployment, installation, registration, health, control, update/rollback, revocation, and safe uninstall. -- Persist a durable Platform aggregate and a Run-local journal so retries, cancellation, stale attempts, lease expiry, and restarts converge instead of reporting synthetic success. -- Authenticate Client Manager directly as a separate component identity with a short-lived session and heartbeat contract, while retaining the distinct singleton component key and generation already used at build time. -- Restrict Run execution to typed deployment and lifecycle operations inside a controlled workspace with checksummed artifacts and declarative executable/health metadata. -- Project safe, useful lifecycle state, real job progress, recovery actions, and audited confirmations into the existing game-operations console. - -**Non-Goals:** - -- Billing, cloud host sales, provider marketplaces, host provisioning, or a general remote administration surface. -- Arbitrary shell commands, plugin-selected host paths, direct browser/plugin access to Run or Client Manager sockets, or reuse of Run sessions/leases/keys for Client Manager. -- Production KMS, public code-signing trust, private-source credential management, or fleet-wide rollout orchestration. Existing envelope encryption and checksum verification remain the bounded first-party mechanisms. -- Claiming production readiness for client-manager fleets, production sandboxing, or later operations-console work. - -## Decisions - -### Decision 1: A durable installation aggregate owns lifecycle state - -Platform adds one `ClientManagerInstallation` per server instance and profile. It references, but is not the same record as, a `ClientManagerDistribution`. The aggregate stores the assigned Run endpoint, target tuple, desired/active/previous artifact and version metadata, current component-key generation, deployment generation, lifecycle status and phase, current job, last successful job, health summary, last seen, retryable failure, and timestamps. Distribution states remain `building`, `available`, `failed`, or `revoked`; installation states cover `requested`, `building`, `available`, `deploying`, `installed`, `registering`, `online`, `degraded`, `offline`, `updating`, `rolling_back`, `stopping`, `uninstalled`, and `failed`. - -All transitions are validated in the service layer and persisted before dispatch. Terminal job projection and component heartbeat advance the aggregate idempotently. A startup/periodic reconciler rebuilds missing projections from durable jobs/sessions and moves timed-out health to `degraded` then `offline` without deleting history. - -Alternative: derive lifecycle state from the latest build job and heartbeat. Rejected because it loses desired state, previous deployment, retries, uninstall history, and restart reconciliation. - -### Decision 2: Run executes typed lifecycle jobs with strict fencing - -Platform uses dedicated job kinds for `client-manager.deploy`, `client-manager.control`, `client-manager.update`, `client-manager.rollback`, and `client-manager.uninstall`. Payloads name logical installation/profile/artifact IDs, target tuple, version/revision, component-key generation, deployment generation, action, checksum, and idempotency key only. Platform accepts them only when user/server/plugin/endpoint authorization, profile capability, endpoint capability, artifact ownership, target/revision compatibility, current key generation, and allowed state transition all agree. - -Run validates the same immutable fields, leases jobs through the existing durable scheduler, and records attempt plus fencing generations in a local journal. Duplicate idempotency keys return the recorded outcome; a stale attempt or deployment generation cannot replace a newer active deployment. Cancellation is checked between artifact chunks and activation steps. Retriable failures retain staging state; permanent validation failures never execute. - -Alternative: model lifecycle as generic commands or reuse game-server lifecycle jobs. Rejected because arbitrary commands are unsafe and client-manager identity/deployment semantics differ from both Run and the managed game server. - -### Decision 3: Deployment uses controlled slots and atomic activation - -Run owns a configured client-manager workspace below its data root. Each installation receives stable internal `active`, `previous`, and `staging/` slots. Artifact bytes are downloaded on the artifact channel with offset/checksum resume metadata, extracted with traversal/link checks, and verified against the Platform checksum before activation. The executable/config paths are resolved from the approved profile/package contract, never from the API caller. Activation uses an atomic rename where supported; the previous slot is kept for one bounded rollback generation. - -Uninstall stops the supervised process, revokes/forgets the local component session material, and removes only the installation's controlled slots and journal entry. It never follows symlinks or deletes server/shared roots. History and audits remain in Platform. - -Alternative: unpack directly over the active files. Rejected because cancellation, partial download, checksum failure, and rollback would leave an indeterminate executable. - -### Decision 4: Client Manager has a separate signed identity and session - -The generated package retains the current client-manager component key and generation. Initial registration signs a canonical request with that key, timestamp, and nonce. Platform resolves the same-server/component key, verifies generation and ownership, rejects expired timestamps or replayed nonces, and checks installed artifact version/revision/capabilities against the active deployment. Successful registration creates a randomly generated short-lived Client Manager session, persists only its hash and metadata, and returns the bearer token only to the Client Manager process. - -Heartbeat and capability reports use that component session, not the Run control session or Run job lease. Sessions are bound to installation ID, server ID, profile, key generation, deployment generation, and active artifact. Reset, explicit revoke, update activation, rollback, uninstall, ownership/endpoint reassignment, or expiry revokes the session. A new deployment must register again. Platform stores bounded replay nonces and prunes them after the signature window. - -Alternative: let Run proxy its own Platform session for the child process. Rejected because it would let a client-manager compromise inherit Run's broader machine authority and would couple component health to Run control traffic. - -### Decision 5: Supervision and health are declarative and bounded - -Profiles declare a fixed executable relative path, fixed argument keys/placeholders, startup timeout, stop timeout, health mode, health interval/timeout, required capability names, and whether start/restart/update/rollback are allowed. Validation rejects shell metacharacters, absolute/traversing paths, environment secrets, raw sockets, and unknown capabilities. Run launches only the approved relative executable from the active slot, captures bounded diagnostics, and reports logical process/health states without PIDs or paths. - -Health can be process-presence or a bounded component self-report contract. Platform uses signed component heartbeats as the authoritative online signal, with Run process state as deployment/control evidence. Missing heartbeats transition online to degraded and then offline according to profile bounds. - -Alternative: accept plugin-provided shell start/health commands. Rejected because it creates an unrestricted execution and data-exfiltration path. - -### Decision 6: Updates are single-installation staged transactions - -An update requires an available current-generation artifact with the same server/profile/target, a compatible declared version/revision, explicit operator approval, and a healthy installed baseline unless force recovery is explicitly allowed. Run downloads and verifies the new artifact in a staging slot, stops the old process only at activation, swaps slots, starts the candidate, and waits for bounded process/component health. Success commits the active/previous references; failure automatically restores the previous slot and reports `rolling_back` followed by the real result. - -Platform rejects revoked artifacts, stale key/deployment generations, cross-target or cross-owner artifacts, and replayed update requests. Restart reconciliation resumes from the durable phase or safely rolls back; it never marks a later phase complete based on a timer. - -Alternative: overwrite and restart immediately. Rejected because it cannot prove health or recover from a broken package. - -### Decision 7: API and UI expose a safe action projection - -Platform provides installation summary/detail, deploy, control, update, rollback, session revoke, retry, and uninstall APIs plus component-only register/heartbeat endpoints. Operator endpoints require the existing session/role/server authorization; component endpoints use the separate signature/session authenticator. Action availability is computed from installed plugin declarations, runtime binding completeness, server ownership, endpoint online/capabilities, distribution state/ownership/target/key generation, installation state, and current user permission. - -platform_web renders a Client Manager operations section on Server Detail and compact availability in server actions. It shows profile, target, desired/active/previous versions, artifact and job IDs, deployment generation, safe health/last-seen reason, build/deploy/register/control/update/rollback/uninstall phases, retry guidance, and destructive confirmations. It never renders a raw key, token, secret ref/value, path, PID, socket, credential, endpoint address, DSN, or RCON password. - -### Decision 8: Auditing and channel isolation are first-class invariants - -Every build/deploy/register/start/stop/restart/update/rollback/revoke/uninstall success, failure, and denial records a durable audit with actor type, safe actor ID, server, profile/component, installation/job/artifact IDs, result, and redacted reason. Component heartbeats are summarized as health state rather than producing an unbounded audit event per pulse. - -Artifact download remains resumable and lower priority; Run control heartbeat, job ack/result/cancel, log spool upload, and optional client-manager traffic use independent workers/queues. Channel-isolation tests exercise a stalled client-manager download and prove the other channels progress. - -## Risks / Trade-offs - -- [Risk] A malicious or compromised source repository can still produce a hostile binary. → Continue requiring approved HTTPS repositories, pinned revisions, fixed build adapters, isolated build workspaces, bounded logs, and explicit operator deployment; do not claim public untrusted builds are production safe. -- [Risk] Atomic rename and executable replacement differ across operating systems. → Keep platform-neutral slot semantics, isolate OS-specific activation in Run, retain the prior slot, and fail without changing active state when atomic activation is unavailable. -- [Risk] Platform and Run can observe different phases during network loss. → Persist intent before dispatch, use idempotency/deployment generations, reconcile job/session/journal state, and favor safe `degraded`/`failed` projections over inferred success. -- [Risk] Key reset immediately invalidates an online component. → Revoke sessions and old distributions, mark the installation as requiring a current-generation rebuild/redeploy, explain recovery in UI, and never silently rotate a package secret. -- [Risk] Heartbeat writes and nonces can grow storage. → Store bounded summaries, unique nonce digests within a short verification window, and prune expired sessions/nonces during reconciliation. -- [Risk] Safe extraction and cleanup are security-sensitive. → Reject traversal, links, device files, unexpected package layouts, and any deletion outside the configured client-manager workspace; cover these cases with tests. - -## Migration Plan - -1. Extend and validate plugin profile declarations without changing existing installed profile records; profiles lacking the new deployment contract remain build/download-only and lifecycle actions are unavailable with a safe reason. -2. Add Platform models/repositories and initialize installation/session/nonce state without mutating existing distributions or component keys. -3. Add typed Platform APIs/jobs/reconciliation and independent component authentication behind capability gating. -4. Add Run protocol/runtime support, controlled workspace/journal, supervisor, update/rollback, and uninstall safety. -5. Enable the full profile for the first-party SCUM example and add platform_web lifecycle management only when the API projection advertises actions. -6. Verify both repositories and all consumers. Rollback hides new actions and stops dispatching lifecycle jobs; existing distribution download remains available and durable lifecycle/audit history is retained. - -## Open Questions - -- Production code-signing, KMS-backed component keys, private repository credentials, multi-node fleet rollout, and long-term deployment artifact retention remain explicit follow-up work. -- The first implementation supports one active installation per server/profile and one retained previous slot; multi-instance client-manager replicas require a later contract. diff --git a/openspec/changes/implement-secure-client-manager-lifecycle/proposal.md b/openspec/changes/implement-secure-client-manager-lifecycle/proposal.md deleted file mode 100644 index ed51ddf..0000000 --- a/openspec/changes/implement-secure-client-manager-lifecycle/proposal.md +++ /dev/null @@ -1,32 +0,0 @@ -## Why - -Client-manager support currently ends after a real package is built and downloaded: Platform does not durably deploy, register, supervise, update, roll back, revoke, or uninstall the companion process. Operators therefore cannot complete a secure build-to-online-to-retired lifecycle or distinguish real machine state from package availability. - -## What Changes - -- Extend plugin client-manager profiles with version/revision, deployment mode, required capabilities, health contract, lifecycle actions, compatibility constraints, and update policy while continuing to reject arbitrary commands and secret-bearing declarations. -- Add a durable Platform client-manager installation aggregate and state machine covering request, build, availability, deployment, installation, registration, online health, degradation/offline detection, update/rollback, stop, failure, revocation, and uninstall history. -- Add typed `client-manager.deploy`, lifecycle control, update, rollback, and uninstall jobs that only an authorized, online, capable Run endpoint may execute for a same-server, same-component, target-compatible, current-generation available artifact. -- Add a client-manager component identity, signed registration/session/heartbeat protocol, capability fencing, expiry/replay protection, and revocation that is separate from Run control registration, jobs, leases, and credentials. -- Add Run-side staged/resumable artifact deployment, checksum verification, atomic activation, bounded process supervision, durable local journals, reconciliation, retry/cancel/idempotency fences, health checks, update rollback, and safe uninstall of controlled workspaces only. -- Add server-list/detail Client Manager management workflows for version/build/deployment/registration/health, start/stop/restart, update/rollback, key reset recovery, failure retry, revoke, and uninstall, backed by real job progress and confirmation flows. -- Add durable audit and safe status projections for every sensitive operation while preventing plugins and platform_web from receiving raw keys, sessions, secret refs/values, host paths, PIDs, sockets, credentials, or direct Run endpoint details. -- Preserve independent control, job, log, artifact, and optional client-manager bridge channels so client downloads and traffic cannot block Run heartbeat, job results, or log upload. - -## Capabilities - -### New Capabilities - -- `secure-client-manager-lifecycle`: Secure deployment, component identity, durable state, health, bounded control, update/rollback, revocation, uninstall, reconciliation, auditing, and operator workflows for plugin-declared client managers. - -### Modified Capabilities - -- `run-distribution-and-client-managers`: Client-manager build artifacts become inputs to a real deployment lifecycle, and key reset/revocation must fence installed instances and require a current-generation redeploy. - -## Impact - -- `plugins/`: manifest schema, SDK/bridge contracts, SCUM and Minecraft examples, unsafe fixtures, validation, and documentation. -- `platform/`: domain/model/repository state, validators, signed component-session protocol, job orchestration, reconciliation, audit, DTOs, API routes, authorization, and documentation. -- Independent `run/` repository: protocol contracts, artifact deployment, local journal/workspace safety, process supervision, health reporting, lifecycle execution, update/rollback, and channel-isolation tests. -- `platform_web/`: API types/schemas/client, Server Detail Client Manager workspace, server action availability, status/progress/confirmation/error states, tests, and browser acceptance. -- No billing, cloud-host/provider marketplace, arbitrary shell, general remote control, production KMS/code-signing, or fleet-orchestration claim is introduced. diff --git a/openspec/changes/implement-secure-client-manager-lifecycle/specs/run-distribution-and-client-managers/spec.md b/openspec/changes/implement-secure-client-manager-lifecycle/specs/run-distribution-and-client-managers/spec.md deleted file mode 100644 index c0d64c8..0000000 --- a/openspec/changes/implement-secure-client-manager-lifecycle/specs/run-distribution-and-client-managers/spec.md +++ /dev/null @@ -1,35 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Run and client-manager keys are isolated singletons -Run executors and plugin-declared client managers SHALL use different authentication secrets, and each server/component SHALL have exactly one current active key stored encrypted in the platform database. A deployed Client Manager SHALL exchange proof of its current component key for a separate short-lived component session and SHALL never use a Run control session or job lease. - -#### Scenario: Client manager is generated after run -- **WHEN** a plugin-declared client-manager package is generated for a server that already has a run package -- **THEN** platform MUST create or reuse the server's current encrypted client-manager key and MUST NOT reuse, reveal through API metadata, or derive it from the run key - -#### Scenario: Component key reset is requested -- **WHEN** an operator resets a server's run key or client-manager key -- **THEN** platform MUST replace the encrypted database key for that component, increment the key generation, revoke all packages generated with prior generations, revoke matching component sessions and installed deployment fences, mark the affected installation as requiring current-generation rebuild and redeploy, and record an audit event identifying the component kind without logging raw key material - -#### Scenario: Old package authenticates after reset -- **WHEN** a run or client-manager package generated before the latest key reset attempts to authenticate, register, heartbeat, deploy, or execute lifecycle work -- **THEN** platform MUST reject the old key, session, artifact, or generation and require the operator to regenerate and redeploy the corresponding run or client-manager package - -### Requirement: Client-manager packages are plugin-declared builds -The platform SHALL support plugin-declared client-manager build profiles for companion executables that require source checkout, configuration injection, and compilation before download or secure lifecycle deployment. Only a real available build artifact with a current component-key generation SHALL be eligible for deployment. - -#### Scenario: SCUM-style client manager is generated -- **WHEN** a plugin declares a client-manager build profile with repository, revision policy, supported target platform, build system, config template, output artifact paths, deployment contract, lifecycle capabilities, health contract, compatibility constraints, and update policy -- **THEN** platform MUST create a run-worker build job that checks out the approved source and revision, injects configuration obtained through the authenticated job-input channel, compiles the target executable, uploads the downloadable artifact through the artifact channel, records deployable version/target/key-generation metadata, and redacts secrets and workspace paths from progress and build results - -#### Scenario: Client-manager build is still running -- **WHEN** the source checkout, environment check, dependency download, compile, artifact upload, or publication stage is incomplete -- **THEN** platform_web MUST display the corresponding real job progress and MUST NOT mark later build or deployment stages complete on a local timer - -#### Scenario: Unsupported client-manager target is requested -- **WHEN** an operator requests a client-manager build for an OS/architecture not declared by the plugin profile -- **THEN** platform MUST reject the request before cloning source or creating a credential - -#### Scenario: Built artifact is selected for deployment -- **WHEN** an operator selects a client-manager distribution for lifecycle deployment -- **THEN** platform MUST require status available, current key generation, matching server/profile/component/target, approved revision and compatibility metadata, authorized server access, complete runtime binding, and an online assigned Run endpoint with the declared deployment capabilities before creating a typed deploy job diff --git a/openspec/changes/implement-secure-client-manager-lifecycle/specs/secure-client-manager-lifecycle/spec.md b/openspec/changes/implement-secure-client-manager-lifecycle/specs/secure-client-manager-lifecycle/spec.md deleted file mode 100644 index 8164e54..0000000 --- a/openspec/changes/implement-secure-client-manager-lifecycle/specs/secure-client-manager-lifecycle/spec.md +++ /dev/null @@ -1,153 +0,0 @@ -## ADDED Requirements - -### Requirement: Plugins declare bounded client-manager lifecycle contracts -Game plugins SHALL declare client-manager version/revision metadata, supported targets, deployment mode, required Run and component capabilities, relative executable contract, bounded lifecycle actions, health contract, compatibility constraints, and update policy before Platform enables lifecycle operations. - -#### Scenario: Valid lifecycle profile is installed -- **WHEN** a plugin declares a client-manager profile with a supported target, pinned or policy-approved revision, fixed build adapter, safe relative executable, bounded start/stop/restart and health settings, and known capability names -- **THEN** plugin and Platform validation MUST preserve the declaration and Platform MUST derive lifecycle availability from the installed declaration, runtime binding, server ownership, and assigned endpoint capabilities - -#### Scenario: Unsafe lifecycle profile is submitted -- **WHEN** a plugin declaration contains arbitrary shell, an absolute or traversing path, raw credentials, secret or token values, direct sockets, host endpoints, environment secrets, unknown capabilities, or an unbounded health/control action -- **THEN** plugin and Platform validation MUST reject it before registration or lifecycle dispatch - -### Requirement: Platform gates every lifecycle action against current ownership and capability state -Platform SHALL authorize client-manager build, deploy, register, control, update, rollback, revoke, retry, and uninstall independently using the current actor, server visibility, installed plugin declaration, runtime binding, assigned Run endpoint, artifact ownership, target, revision, component key generation, and lifecycle state. - -#### Scenario: Authorized owner deploys an available build -- **WHEN** a server owner or authorized administrator selects an available current-generation distribution for the same server, profile, target, and approved revision while the assigned Run endpoint is online and declares client-manager deployment capability -- **THEN** Platform MUST create or reuse one typed deployment intent and job and MUST expose its real state and progress - -#### Scenario: Mismatched lifecycle input is requested -- **WHEN** an actor supplies another owner's server, another server or component artifact, another Run endpoint, a mismatched target or revision, an expired or revoked distribution, or a stale component-key generation -- **THEN** Platform MUST deny the operation before job creation, MUST record a redacted denial audit, and MUST NOT reveal whether an inaccessible resource exists - -#### Scenario: Service or component credential calls an operator endpoint -- **WHEN** a Run service credential or Client Manager component session calls an operator lifecycle endpoint without the required operator role -- **THEN** Platform MUST return an authorization failure and MUST NOT broaden that credential into an operator session - -### Requirement: Platform persists and reconciles a real lifecycle state machine -Platform SHALL durably persist desired state, active and previous deployment references, component-key and deployment generations, job linkage, health summary, failure detail, and lifecycle timestamps for each server/profile installation. - -#### Scenario: Lifecycle advances through real evidence -- **WHEN** build, deployment, registration, control, update, rollback, or uninstall work changes phase -- **THEN** Platform MUST transition only through valid requested, building, available, deploying, installed, registering, online, degraded, offline, updating, rolling_back, stopping, uninstalled, or failed states using durable job results, Run reports, or authenticated component heartbeats rather than local UI timers - -#### Scenario: Platform restarts with in-flight work -- **WHEN** Platform restarts while a lifecycle job or component session is in progress -- **THEN** reconciliation MUST restore the persisted intent, project the durable job/session state idempotently, reject stale attempts, and either resume, retry, roll back, or fail safely without creating a duplicate activation - -#### Scenario: Duplicate lifecycle request is retried -- **WHEN** the same actor repeats a request with the same idempotency key and immutable inputs -- **THEN** Platform MUST return the original installation/job result, while the same idempotency key with different immutable inputs MUST be rejected - -### Requirement: Run deploys client managers through a checksummed controlled workspace -Run SHALL execute client-manager deployment only through the typed job contract and SHALL download, resume, verify, stage, and atomically activate an authorized distribution inside its configured client-manager workspace. - -#### Scenario: Deployment completes after an interrupted transfer -- **WHEN** a current leased deployment downloads an available artifact in chunks and the transfer is interrupted -- **THEN** Run MUST persist offset and checksum state, resume without re-downloading acknowledged bytes, verify the final checksum and safe package layout, activate the staged slot, and report installed only after real activation succeeds - -#### Scenario: Deployment payload or package is unsafe -- **WHEN** a deployment contains arbitrary commands, raw host paths, sockets, credentials, a stale attempt/deployment/key generation, a mismatched artifact/target/component, a checksum failure, traversal, symlink, device file, or unexpected executable layout -- **THEN** Run MUST reject or fail the job without changing the active slot and MUST return only bounded redacted diagnostics - -#### Scenario: Deployment is cancelled or retried -- **WHEN** cancellation arrives between chunks or activation phases, a lease expires, or a retry uses the same idempotency and deployment generation -- **THEN** Run MUST honor the current fence, retain only safe resumable staging state, never let a stale attempt replace a newer activation, and converge on one recorded outcome - -### Requirement: Client Manager authenticates as an independent component -Client Manager SHALL register with Platform using its own current component key and generation and SHALL receive a short-lived component session that is separate from Run control registration, job leases, credentials, and channels. - -#### Scenario: Installed component registers successfully -- **WHEN** a deployed Client Manager signs a canonical registration request with a fresh timestamp and nonce and reports the active installation, artifact, version/revision, deployment generation, and declared capabilities -- **THEN** Platform MUST verify the same server/profile ownership, current component-key generation, active deployment, target/revision, signature, nonce, and capabilities, persist only a hash of a new expiring component session, return the token only to the component, and move the installation toward online health - -#### Scenario: Registration signature is stale, replayed, revoked, or mismatched -- **WHEN** registration uses an expired timestamp, repeated nonce, revoked or previous-generation key, another server/component identity, inactive artifact, stale deployment generation, or undeclared capabilities -- **THEN** Platform MUST reject registration, record a safe denial audit, and MUST NOT create or reveal a component session - -#### Scenario: Run identity is presented as Client Manager identity -- **WHEN** a caller presents a Run key, Run bearer session, Run job lease, or Run endpoint identity to the Client Manager registration or heartbeat contract -- **THEN** Platform MUST reject it and MUST NOT reuse Run authentication state - -### Requirement: Component heartbeats drive safe health projection -Platform SHALL accept bounded heartbeat and capability reports only from a valid component session and SHALL project logical health and last-seen state without exposing local process details. - -#### Scenario: Healthy component heartbeat arrives -- **WHEN** an unexpired, unrevoked session bound to the active installation reports a monotonic heartbeat with declared capabilities and a safe health code -- **THEN** Platform MUST update last seen and logical health idempotently and MUST expose only version, status, health code/reason, capabilities, and timestamps to authorized operators - -#### Scenario: Heartbeat expires -- **WHEN** a component misses its declared heartbeat grace and offline thresholds -- **THEN** reconciliation MUST transition the installation from online to degraded and then offline using safe reasons while preserving the last successful deployment and audit history - -#### Scenario: Session heartbeat is replayed or fenced -- **WHEN** a heartbeat sequence repeats, the session is expired/revoked, or its key, deployment, endpoint ownership, or artifact fence is no longer current -- **THEN** Platform MUST reject it without mutating health and require a new valid registration - -### Requirement: Run performs bounded client-manager process control -Run SHALL start, stop, restart, and inspect a deployed Client Manager only through the plugin-declared executable and health contract and the typed lifecycle job. - -#### Scenario: Operator starts or restarts a deployed component -- **WHEN** Platform dispatches an authorized current-generation control job whose action is declared by the profile -- **THEN** Run MUST supervise the fixed relative executable from the active slot, use bounded timeouts, persist the logical process state, and report progress and outcome without returning a PID, host path, environment secret, or socket - -#### Scenario: Unsupported or stale control is requested -- **WHEN** a control action is undeclared, the installation is uninstalled, the attempt or deployment generation is stale, or another process already owns the active fence -- **THEN** Run MUST reject the operation idempotently without executing a command or disrupting the newer process - -### Requirement: Updates are staged, health-checked, and rollback-safe -Platform and Run SHALL treat a Client Manager update as a same-installation transaction with explicit approval, compatible current-generation artifact selection, staged activation, bounded health confirmation, and a retained previous deployment. - -#### Scenario: Compatible update becomes healthy -- **WHEN** an authorized operator approves a newer compatible artifact for the same server/profile/target and Run verifies, stages, activates, starts, and observes required health -- **THEN** Platform MUST set the new artifact/version as active, retain the prior deployment as rollback candidate, revoke the superseded component session, require new registration, and record real update progress and audit evidence - -#### Scenario: Candidate update fails health -- **WHEN** download, checksum, activation, startup, registration, or health confirmation fails after an update begins -- **THEN** Run MUST preserve or restore the previous slot, Platform MUST project rolling_back and the real rollback result, and success MUST NOT be reported unless the restored deployment is active and healthy - -#### Scenario: Invalid update or rollback is requested -- **WHEN** an artifact is revoked, from another server/profile/target, has a stale key generation, violates compatibility/version policy, or the previous slot no longer exists -- **THEN** Platform and Run MUST reject the request before activation and preserve the current deployment - -### Requirement: Revocation and uninstall are safe and idempotent -Platform SHALL support session revocation and Run SHALL stop and uninstall a Client Manager without deleting server or shared files, while retaining Platform lifecycle and audit history. - -#### Scenario: Key or session is revoked -- **WHEN** an authorized operator resets the component key, explicitly revokes the component session, reassigns ownership/endpoint, activates an update/rollback, or begins uninstall -- **THEN** Platform MUST revoke matching sessions, reject subsequent heartbeats, fence old artifacts/deployments as applicable, and show that rebuild/redeploy or registration is required - -#### Scenario: Installed component is uninstalled -- **WHEN** an authorized operator confirms uninstall and Run completes the typed job -- **THEN** Run MUST stop the supervised process, remove only controlled active/previous/staging slots and local session/journal material for that installation, Platform MUST mark it uninstalled, and build/distribution/audit history MUST remain available - -#### Scenario: Uninstall is repeated or interrupted -- **WHEN** uninstall is retried after partial cleanup, cancellation, lease expiry, or an already-uninstalled result -- **THEN** Run and Platform MUST converge idempotently without following links, escaping the configured workspace, or deleting game server/shared data - -### Requirement: Lifecycle operations are durably audited and redacted -Platform SHALL record durable success, failure, and denial audits for build, deploy, register, start, stop, restart, update, rollback, revoke, retry, and uninstall using safe identifiers and bounded reasons. - -#### Scenario: Lifecycle result is audited -- **WHEN** an operator, Run endpoint, or Client Manager performs or is denied a sensitive lifecycle action -- **THEN** the audit MUST include actor type and safe actor ID, server/profile/component, installation, job or artifact ID where applicable, operation, result, and redacted reason without raw keys, tokens, secret refs/values, credentials, paths, PIDs, sockets, endpoint addresses, DSNs, RCON passwords, or large output - -### Requirement: Client Manager traffic remains isolated from Run channels -Client-manager registration, heartbeat, deployment transfer, process control, and optional game-client traffic SHALL remain separated from Run control heartbeat, job acknowledgement/result/cancel, log ingest, and artifact upload scheduling. - -#### Scenario: Client-manager artifact transfer stalls -- **WHEN** a large or stalled client-manager download or component traffic stream is active -- **THEN** Run heartbeat, job ack/result/cancel polling, log spool upload, and unrelated artifact progress MUST continue independently within their bounded queues - -### Requirement: Platform web provides a complete safe Client Manager workspace -platform_web SHALL provide authorized operators a Client Manager management workspace that reflects real backend state and preserves the existing black-mecha and magical-girl crystal-moonlight game-operations visual system. - -#### Scenario: Operator manages the complete lifecycle -- **WHEN** an authorized operator opens Server Detail for a declared Client Manager -- **THEN** the UI MUST show safe profile/target/version/revision, build and artifact state, deployment/registration/online health, last seen, active/previous deployment, current job progress, permitted start/stop/restart, update/rollback, retry/redeploy after key reset, session revoke, and confirmed uninstall actions using real API projections - -#### Scenario: Action is unavailable or destructive -- **WHEN** an action lacks permission, declaration, binding, online endpoint, capability, compatible artifact, current key generation, allowed lifecycle state, or confirmation -- **THEN** the UI MUST disable or hide it with a safe reason, require explicit confirmation for key reset/revoke/rollback/uninstall, preserve 401/403 handling, and MUST NOT fabricate progress or expose raw secrets, sessions, paths, PIDs, sockets, credentials, or endpoint addresses diff --git a/openspec/changes/implement-secure-client-manager-lifecycle/tasks.md b/openspec/changes/implement-secure-client-manager-lifecycle/tasks.md deleted file mode 100644 index 8b2a92b..0000000 --- a/openspec/changes/implement-secure-client-manager-lifecycle/tasks.md +++ /dev/null @@ -1,64 +0,0 @@ -## 1. Plugin Lifecycle Contracts - -- [x] 1.1 Extend client-manager manifest and SDK types with safe version/revision, deployment, executable, lifecycle capability, health, compatibility, and update-policy declarations. -- [x] 1.2 Validate bounded relative executables, target/capability enums, timeouts, version rules, and reject arbitrary shell, traversal, raw secrets, endpoints, sockets, and credential-bearing declarations. -- [x] 1.3 Update SCUM and Minecraft example profiles, SDK bridge contracts, docs, and unsafe fixtures for complete lifecycle declarations and safe lifecycle requests/status. -- [x] 1.4 Add plugin manifest, SDK, and bridge tests covering accepted contracts, unsafe declarations, typed operations, and redaction. - -## 2. Platform Durable Lifecycle Model - -- [x] 2.1 Add domain/model/DTO types for installation states, action availability, deployment slots, health, desired/active/previous versions, lifecycle requests/results, component sessions, and replay nonces. -- [x] 2.2 Extend repository interfaces plus file and MySQL stores with durable client-manager installations, sessions, nonce fences, idempotent lookups, list/update/revoke operations, and safe persistence tests. -- [x] 2.3 Add validators for lifecycle transitions, action/target/version compatibility, deployment/control/update/rollback/uninstall inputs, component registration, heartbeat sequences, and redacted bounded results. -- [x] 2.4 Implement installation state transitions and terminal job projection using real durable job evidence, including idempotency, stale attempt/deployment/key fences, retryable failure, and active/previous deployment commits. -- [x] 2.5 Implement startup/periodic reconciliation for in-flight lifecycle jobs, expired sessions/nonces, heartbeat degraded/offline thresholds, restart recovery, and key-reset/endpoint-reassignment fencing. - -## 3. Independent Component Identity - -- [x] 3.1 Add canonical Client Manager registration signature and session contracts that are separate from Run control registration and job leases. -- [x] 3.2 Implement current component-key/generation signature verification, timestamp/nonce replay protection, ownership/artifact/target/revision/deployment/capability checks, hashed expiring session issuance, and denied audits. -- [x] 3.3 Implement component-session heartbeat authentication, monotonic sequence fencing, safe health projection, expiry/revocation, and explicit session revoke behavior. -- [x] 3.4 Add registration/session/heartbeat tests for current identity plus cross-owner/server/component/artifact/target/revision/key generation, expired, revoked, replayed, and Run-credential rejection paths. - -## 4. Platform Job Orchestration and APIs - -- [x] 4.1 Add typed client-manager deploy, control, update, rollback, and uninstall job kinds, payload validation, endpoint capability declarations, and safe job progress/result projection. -- [x] 4.2 Implement service authorization and action gating across actor role, server visibility, installed plugin/profile, runtime binding, endpoint online/capabilities, distribution availability/ownership/target/revision/key generation, and lifecycle state. -- [x] 4.3 Implement deploy/control/update/rollback/retry/revoke/uninstall services with durable intent-before-dispatch, idempotency, cancel/retry/stale-attempt handling, session fencing, and audit events. -- [x] 4.4 Add operator lifecycle summary/detail/action routes and component register/heartbeat routes with named DTOs, OpenAPI-style comments, session separation, safe errors, and API documentation. -- [x] 4.5 Add platform service/API tests covering owner/admin/service auth, 401/403, build-to-deploy, cross-boundary denial, restart reconcile, cancellation/retry/idempotency, health timeout, update rollback, key reset recovery, uninstall safety, auditing, and redaction. - -## 5. Run Deployment and Supervision - -- [x] 5.1 Add Run protocol payloads and validation for client-manager deploy/control/update/rollback/uninstall, immutable fences, lifecycle progress/results, and endpoint capabilities. -- [x] 5.2 Add a scoped client-manager workspace and durable local journal for installation slots, chunk offsets/checksums, deployment/attempt/key generations, idempotency outcomes, process state, and restart reconciliation. -- [x] 5.3 Implement resumable artifact download, checksum verification, safe archive extraction, staging, atomic active/previous activation, cancellation checkpoints, and rejection of traversal/symlinks/device files/unexpected layouts. -- [x] 5.4 Implement bounded declarative client-manager start/stop/restart/status supervision with fixed relative executable, safe timeouts, logical health, and no path/PID/socket projection. -- [x] 5.5 Implement staged update health confirmation, automatic rollback, explicit rollback, stale/revoked generation rejection, re-entry after restart, and real phase reporting. -- [x] 5.6 Implement idempotent safe uninstall that stops the process and deletes only controlled installation slots/journal/session material without following links or touching server/shared files. -- [x] 5.7 Add Run tests for deploy resume/checksum, fences, cancel/retry/stale attempts, reconciliation, supervision, health failure rollback, uninstall safety, redaction, and channel isolation under stalled client-manager traffic. - -## 6. platform_web Lifecycle Workspace - -- [x] 6.1 Add API types, schemas, client methods, safe action projections, polling/job progress integration, and tests for Client Manager lifecycle summaries and commands. -- [x] 6.2 Build a rich Server Detail Client Manager workspace showing build/artifact, desired/active/previous version, deployment/registration/online health, last seen, real current job phases, retry guidance, and action availability. -- [x] 6.3 Add start/stop/restart, deploy/redeploy, update/rollback, session revoke, key reset recovery, retry, and uninstall confirmation/error flows while preserving compact server action menus and both existing themes. -- [x] 6.4 Add frontend tests and browser acceptance for full state/action coverage, real progress/failure recovery, destructive confirmations, 401/403 behavior, responsive layouts, theme preservation, and secret/path/PID/socket redaction. - -## 7. Documentation and Verification - -- [x] 7.1 Update Platform, Run, plugins, SDK, platform_web, route, domain, protocol, and deployment docs with lifecycle states, security/session boundaries, operations, recovery, and explicit production non-goals. -- [x] 7.2 Run plugin manifest validation, plugin SDK/tests/typecheck, Platform `go test -count=1 ./...`, independent Run `go test -count=1 ./...`, and focused race/restart checks where practical. -- [x] 7.3 Run platform_web tests, typecheck, production build, and a browser walkthrough for the touched Server Detail and server action workflows in both visual themes. -- [x] 7.4 Run shell/compose checks, `scripts/check-structure.sh`, and `git diff --check` in both the main repository and independent Run checkout. -- [x] 7.5 Run `openspec validate implement-secure-client-manager-lifecycle --strict` and record all verification evidence below before marking implementation complete. - -## Verification Evidence - -- `plugins`: `npm run typecheck` passed; `npm test` passed (1 file, 18 tests); `npm run validate:manifest` passed for dev, SCUM, and Minecraft manifests (tsx IPC required the approved escalated run). -- `platform`: `go test -count=1 ./...` passed for api/config/domain/dto/model/repo/service/validator packages. -- independent `run`: `go test -count=1 ./...` passed for api/config/protocol/runtime/spool; Client Manager tests cover chunk resume/checksum, restart journal resume, traversal archive rejection, idempotency, stale fences, automatic update restore, safe uninstall, redaction-safe results, and controlled supervisor paths. -- `platform_web`: `npm run typecheck`, `npm test` (20 files, 111 tests), and `npm run build` passed. Added lifecycle API/schema tests, 401/403 behavior remains covered by existing client tests, and source assertions cover secret/path/PID/socket redaction and confirmation flows. -- Browser walkthrough: local Platform-backed console at `http://127.0.0.1:5174/` logged in as the seeded operator, opened `SCUM Alpha` Server Detail, verified the `Client Manager 生命周期` panel and real empty-state gating, switched magical-girl and black-mecha palettes, and checked the panel at 390x844 (`362px` wide, no horizontal overflow). -- Shell/compose: `for file in scripts/*.sh; do bash -n "$file"; done` passed; `docker compose config` passed; `scripts/check-structure.sh` passed; `git diff --check` passed in the main repository and independent run checkout. -- `openspec validate implement-secure-client-manager-lifecycle --strict` passed. diff --git a/openspec/changes/implement-server-management-workflows/.openspec.yaml b/openspec/changes/implement-server-management-workflows/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/implement-server-management-workflows/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/implement-server-management-workflows/design.md b/openspec/changes/implement-server-management-workflows/design.md deleted file mode 100644 index 60780e5..0000000 --- a/openspec/changes/implement-server-management-workflows/design.md +++ /dev/null @@ -1,87 +0,0 @@ -## 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. diff --git a/openspec/changes/implement-server-management-workflows/proposal.md b/openspec/changes/implement-server-management-workflows/proposal.md deleted file mode 100644 index 98eaafb..0000000 --- a/openspec/changes/implement-server-management-workflows/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -Server management is the next first-party workflow after the console shell, plugin registry, run job channel, log ingest, and artifact transfer are available. Operators need a complete platform-mediated path to create a server instance from an installed game plugin, start it, stop it, and observe the resulting lifecycle state without exposing run internals to the browser or plugin pages. - -## What Changes - -- Add server lifecycle action APIs for create/install, start, and stop workflows. -- Dispatch lifecycle work through the existing platform job channel using bounded job metadata and idempotency keys. -- Enforce plugin, run endpoint, instance state, and optimistic config-version validation before lifecycle dispatch. -- Project lifecycle job state back onto server instances so the platform and frontend can show actionable states. -- Add frontend server management views and API client methods for create, start, stop, refresh, and workflow status. -- Add run-side lifecycle executor support for installing, starting, and stopping server jobs without exposing host paths, raw credentials, or direct sockets. - -## Capabilities - -### New Capabilities - -- `server-management-workflows`: Platform-mediated server instance create, start, stop, and status workflows across `platform/`, `run/`, and `platform_web/`. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` domain, DTO, validator, service, repository, API handlers, route docs, and protocol docs for server lifecycle operations. -- Affects `run/` protocol/client/executor code for lifecycle job handling. -- Affects `platform_web/` API types/client methods, route/page contracts, server management components, tests, and browser walkthrough. -- Reuses existing game plugin registry, run control, run job, log ingest, and artifact transfer contracts; does not add billing, cloud host sales, raw AI key exposure, host-path exposure, or direct plugin-to-run access. diff --git a/openspec/changes/implement-server-management-workflows/specs/server-management-workflows/spec.md b/openspec/changes/implement-server-management-workflows/specs/server-management-workflows/spec.md deleted file mode 100644 index 58cae19..0000000 --- a/openspec/changes/implement-server-management-workflows/specs/server-management-workflows/spec.md +++ /dev/null @@ -1,79 +0,0 @@ -## ADDED Requirements - -### Requirement: Server create workflow dispatches install job -The platform SHALL provide a server create workflow that validates an installed game plugin, a compatible run endpoint, and a non-empty idempotency key before creating a server instance and dispatching a queued install job through the job channel. - -#### Scenario: Create workflow accepted -- **WHEN** an operator submits a create workflow with an installed plugin, an online compatible run endpoint, a server name, and an idempotency key -- **THEN** the platform MUST create a server instance in `installing` state and create a queued `process.install` job bound to that instance and run endpoint - -#### Scenario: Create workflow rejects invalid dependencies -- **WHEN** an operator submits a create workflow with a missing plugin, disabled plugin, offline run endpoint, or run endpoint missing required capabilities -- **THEN** the platform MUST reject the workflow and MUST NOT dispatch a lifecycle job - -### Requirement: Server start workflow dispatches start job -The platform SHALL provide a start workflow for an existing server instance that validates the instance state, config version, plugin lifecycle action, run endpoint status, run endpoint capability, and idempotency key before dispatching a queued start job. - -#### Scenario: Start workflow accepted -- **WHEN** an operator starts a `ready` or `stopped` server instance with the current config version and an idempotency key -- **THEN** the platform MUST create a queued `process.start` job for the instance and return both the instance and job metadata - -#### Scenario: Start workflow rejects stale config -- **WHEN** an operator starts a server instance with an expected config version that does not match the instance config version -- **THEN** the platform MUST reject the workflow and MUST NOT dispatch a lifecycle job - -### Requirement: Server stop workflow dispatches stop job -The platform SHALL provide a stop workflow for an existing running server instance that validates the instance state, config version, plugin lifecycle action, run endpoint status, run endpoint capability, and idempotency key before dispatching a queued stop job. - -#### Scenario: Stop workflow accepted -- **WHEN** an operator stops a `running` server instance with the current config version and an idempotency key -- **THEN** the platform MUST create a queued `process.stop` job for the instance and return both the instance and job metadata - -#### Scenario: Stop workflow rejects non-running instance -- **WHEN** an operator stops a server instance that is not `running` -- **THEN** the platform MUST reject the workflow and MUST NOT dispatch a lifecycle job - -### Requirement: Lifecycle job results update server instance state -The platform SHALL project terminal lifecycle job results onto the associated server instance after accepting a run job result. - -#### Scenario: Install result marks ready -- **WHEN** run completes a `process.install` lifecycle job successfully -- **THEN** the platform MUST mark the associated server instance `ready` - -#### Scenario: Start result marks running -- **WHEN** run completes a `process.start` lifecycle job successfully -- **THEN** the platform MUST mark the associated server instance `running` - -#### Scenario: Stop result marks stopped -- **WHEN** run completes a `process.stop` lifecycle job successfully -- **THEN** the platform MUST mark the associated server instance `stopped` - -#### Scenario: Failed lifecycle result marks failed -- **WHEN** run completes an install, start, or stop lifecycle job as failed or cancelled -- **THEN** the platform MUST mark the associated server instance `failed` - -### Requirement: Run lifecycle executor is bounded -The run executor SHALL support only declared lifecycle capabilities for install, start, and stop jobs and MUST return bounded metadata-only results without raw host paths, raw credentials, or direct socket details. - -#### Scenario: Supported lifecycle job handled -- **WHEN** run receives a job assignment for `process.install`, `process.start`, or `process.stop` -- **THEN** the lifecycle executor MUST produce a successful bounded result suitable for the job result channel - -#### Scenario: Unsupported lifecycle job rejected -- **WHEN** run receives a job assignment for an unsupported lifecycle capability -- **THEN** the lifecycle executor MUST return a failed bounded result without executing local commands - -### Requirement: Server management UI supports create start and stop -The frontend SHALL expose server management controls that use platform workflow APIs to create, start, stop, and refresh server instances without receiving run credentials, raw host paths, raw AI keys, or direct sockets. - -#### Scenario: UI creates server workflow -- **WHEN** an operator submits the server management create form -- **THEN** the frontend MUST call the platform create workflow API and render the returned instance and lifecycle job status - -#### Scenario: UI starts and stops server -- **WHEN** an operator clicks start or stop for an eligible server instance -- **THEN** the frontend MUST call the matching platform workflow API with the current config version and render the returned lifecycle job status - -#### Scenario: UI refreshes workflow status -- **WHEN** the server management page refreshes data -- **THEN** the frontend MUST read server instances, jobs, plugins, and run endpoints through platform APIs and MUST NOT display raw secrets, host paths, run credentials, or direct sockets diff --git a/openspec/changes/implement-server-management-workflows/tasks.md b/openspec/changes/implement-server-management-workflows/tasks.md deleted file mode 100644 index 4729de1..0000000 --- a/openspec/changes/implement-server-management-workflows/tasks.md +++ /dev/null @@ -1,43 +0,0 @@ -## 1. Platform Lifecycle Workflows - -- [x] 1.1 Add lifecycle domain, DTO, validator, and conversion contracts for create/start/stop workflow requests and responses. -- [x] 1.2 Implement platform service methods for create/install, start, and stop workflow validation and job dispatch. -- [x] 1.3 Project accepted terminal lifecycle job results onto server instance state. -- [x] 1.4 Add platform HTTP routes and OpenAPI-style comments for create/start/stop lifecycle workflow actions. -- [x] 1.5 Update platform route/protocol/domain documentation for implemented server lifecycle workflows. -- [x] 1.6 Add platform service/API tests for accepted create/start/stop workflows, rejected invalid state/stale config, and lifecycle result state projection. - -## 2. Lifecycle Capabilities and Run Executor - -- [x] 2.1 Add `process.install` to plugin manifest schema, plugin SDK capability types, platform validation allowlists, examples, and fixtures where lifecycle install support is required. -- [x] 2.2 Add run lifecycle executor support for bounded install/start/stop job handling without host paths, raw credentials, or direct sockets. -- [x] 2.3 Add run tests for supported lifecycle jobs, unsupported lifecycle jobs, and smoke capability reporting. -- [x] 2.4 Update run protocol/runtime documentation for lifecycle executor scope. - -## 3. Frontend Server Management - -- [x] 3.1 Add frontend API contracts and client methods for run endpoints, jobs, and create/start/stop server lifecycle workflows. -- [x] 3.2 Add frontend server management view contracts and request builders outside page components. -- [x] 3.3 Implement the server management page create form, refresh action, start/stop actions, pending job display, and safe fallback data. -- [x] 3.4 Add frontend tests for API client calls and server management page rendering without unsafe fields. - -## 4. Verification - -- [x] 4.1 Run `go test ./...` from `platform/` and record evidence. -- [x] 4.2 Run `go test ./...` from `run/` and record evidence. -- [x] 4.3 Run `cd plugins && npm run typecheck && npm run test && npm run validate:manifest` and record evidence. -- [x] 4.4 Run `cd platform_web && npm run typecheck && npm run test && npm run build` and record evidence. -- [x] 4.5 Run browser walkthrough for server management create/start/stop UI at desktop and mobile widths. -- [x] 4.6 Run `scripts/check-structure.sh` and record evidence. -- [x] 4.7 Run `openspec validate implement-server-management-workflows --strict` and record evidence. - -## Evidence - -- 2026-07-03: `cd platform && go test ./...` passed after platform lifecycle service/API implementation. -- 2026-07-03: `cd run && go test ./...` passed after run lifecycle executor implementation. -- 2026-07-03: `cd plugins && npm run typecheck && npm run test && npm run validate:manifest` passed after adding `process.install`. -- 2026-07-03: Frontend API client/contracts, server management view contracts, request builders, page workflow UI, styling, docs, and tests were updated for run endpoints, jobs, create/start/stop workflows, pending job display, and safe fallback data. -- 2026-07-03: `cd platform_web && npm run typecheck && npm run test && npm run build` passed; Vitest reported 6 files / 14 tests passed and Vite built production assets. -- 2026-07-03: Browser walkthrough via Chrome DevTools at `http://127.0.0.1:4173/#/servers` passed for desktop create/install, start, stop, no unsafe fields visible, and mobile single-column server workspace. -- 2026-07-03: `scripts/check-structure.sh` passed. -- 2026-07-03: `openspec validate implement-server-management-workflows --strict` passed. diff --git a/openspec/changes/improve-server-terminal-log-window/.openspec.yaml b/openspec/changes/improve-server-terminal-log-window/.openspec.yaml deleted file mode 100644 index 878dc31..0000000 --- a/openspec/changes/improve-server-terminal-log-window/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-07 diff --git a/openspec/changes/improve-server-terminal-log-window/design.md b/openspec/changes/improve-server-terminal-log-window/design.md deleted file mode 100644 index b09564a..0000000 --- a/openspec/changes/improve-server-terminal-log-window/design.md +++ /dev/null @@ -1,34 +0,0 @@ -## Context - -The server log SSE route currently replays `historyLimit` entries independently for every stream, then sends live events. The management terminal stores a bounded client buffer but does not own a scroll container or follow state, so the browser viewport remains at its initial top position after history is appended. Durable log bodies are stored by the platform `LogBodyStore` as memory indexes backed by per-stream JSONL segment files; this change does not rewrite or delete that durable history. - -## Goals / Non-Goals - -**Goals:** - -- Make initial server log replay bounded across all streams and limited to the newest entries. -- Keep replay chronological and preserve live SSE delivery. -- Keep at most 10,000 terminal lines in the browser and automatically follow the newest line until the operator scrolls away. -- Make returning to the bottom restore follow mode. - -**Non-Goals:** - -- No full-history fetch, log retention deletion, database migration, or change to Run ingest. -- No browser-to-Run transport or game-specific log selection. - -## Decisions - -- Treat `historyLimit` as a server-wide budget for the SSE endpoint, with a platform cap of 10,000. This prevents stream-count multiplication while retaining the existing query parameter and compatibility for existing clients. -- Read bounded tails from each stream using its latest sequence, merge by timestamp/sequence/stream ID, and emit only the newest budgeted entries in chronological order. This keeps the UI output coherent without adding a new cross-stream database query API. -- Give the terminal output element a ref and track `followLatest` from scroll position. The terminal requests only a 500-entry recent replay, then locks to the bottom after `ready` with two animation frames; layout-driven scroll events during replay cannot unlock follow mode. After initialization, a user scroll above a small bottom threshold unlocks, and a later scroll to the threshold locks again. -- Keep the rendered buffer capped at 10,000 through the existing merge helper. System and command-result lines use the same cap, so browser memory remains bounded even when the stream is noisy. - -## Risks / Trade-offs - -- [Risk] Reading a tail from every stream still does bounded work proportional to stream count. -> Mitigation: each stream read is capped by the global budget and the final response is capped at 10,000; existing per-stream cursor storage remains unchanged. -- [Risk] Timestamp ties across streams can reorder entries relative to ingest order. -> Mitigation: use sequence and stream ID tie-breakers and retain each stream's sequence ordering. -- [Risk] Scroll events can race with React rendering. -> Mitigation: defer bottom scrolling with `requestAnimationFrame` and re-check the element's current scroll metrics. - -## Migration Plan - -Deploy the backend and frontend together. Existing clients sending `historyLimit` continue to work, but receive a server-wide bounded replay. Rollback is code-only: reverting the endpoint selection and terminal follow logic restores the prior behavior without data migration. diff --git a/openspec/changes/improve-server-terminal-log-window/proposal.md b/openspec/changes/improve-server-terminal-log-window/proposal.md deleted file mode 100644 index a938efa..0000000 --- a/openspec/changes/improve-server-terminal-log-window/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -## Why - -Opening the management terminal currently places an operator at the top of the replayed history, so the oldest item in the selected window is visible instead of the newest runtime output. The SSE history limit is also applied independently to every log stream, allowing the initial response size to grow with stream count instead of remaining bounded for the server view. - -## What Changes - -- Bound SSE history replay by a server-wide recent-entry window, capped at 10,000 entries, rather than replaying the requested limit for each stream. -- Preserve chronological replay order so a browser can render the selected recent window and begin at its newest output. -- Make the management terminal retain at most 10,000 rendered lines, initially follow the bottom, pause auto-follow when an operator scrolls away, and resume it when the operator returns to the bottom. -- Keep `POST /api/v1/log-streams/query` as the bounded, explicit historical cursor API; do not add full-history browser loading. - -## Capabilities - -### New Capabilities - -- `bounded-server-log-view`: Bounded server-wide SSE history replay and management-terminal auto-follow behavior for live server log views. - -### Modified Capabilities - -- None. - -## Impact - -- Affects the Platform server log SSE endpoint, its Go tests, and the persisted-log history selection path. -- Affects the platform_web log event client types and management terminal component/tests. -- Does not change Run-to-Platform durable batch ingest, browser authorization, plugin-declared log sources, or game-specific behavior. diff --git a/openspec/changes/improve-server-terminal-log-window/specs/bounded-server-log-view/spec.md b/openspec/changes/improve-server-terminal-log-window/specs/bounded-server-log-view/spec.md deleted file mode 100644 index 5657e37..0000000 --- a/openspec/changes/improve-server-terminal-log-window/specs/bounded-server-log-view/spec.md +++ /dev/null @@ -1,46 +0,0 @@ -## ADDED Requirements - -### Requirement: Server SSE history is globally bounded -The Platform SHALL interpret the server log SSE `historyLimit` as a total recent-entry budget across all streams, default it to 100 entries, and cap it at 10,000 entries. It MUST NOT load or transmit the complete durable history when opening a browser stream. - -#### Scenario: Terminal opens with the default window -- **WHEN** an authorized operator opens `/api/v1/server-instances/{id}/logs/events` without `historyLimit` -- **THEN** Platform replays no more than 100 recent entries total across that server's streams -- **AND** the replay contains the newest available entries rather than starting at sequence one - -#### Scenario: Requested history is capped -- **WHEN** a client requests a `historyLimit` greater than 10,000 -- **THEN** Platform limits the total replay to 10,000 entries -- **AND** it does not perform an unbounded history query - -#### Scenario: Replay order is chronological -- **WHEN** multiple streams have entries in the bounded history window -- **THEN** Platform emits the selected entries in ascending timestamp, sequence, and stream-ID tie-break order -- **AND** emits `ready` only after the bounded replay completes - -### Requirement: Management terminal follows the latest output -The management terminal SHALL begin with the output viewport at the newest rendered line and remain pinned to the bottom while follow mode is locked. User scrolling away from the bottom SHALL unlock follow mode, and scrolling back to the bottom SHALL lock it again. - -#### Scenario: Initial history opens at the newest line -- **WHEN** the terminal receives its initial bounded history -- **THEN** the output viewport scrolls to the bottom after the lines render -- **AND** new log events continue to appear without moving the viewport away from the newest line -- **AND** the terminal requests no more than 500 initial history entries while retaining up to 10,000 rendered lines as live output arrives - -#### Scenario: Operator inspects older output -- **WHEN** the operator scrolls above the bottom threshold -- **THEN** follow mode unlocks -- **AND** incoming log events are retained in the buffer without forcing a scroll - -#### Scenario: Operator returns to live output -- **WHEN** the operator scrolls back within the bottom threshold -- **THEN** follow mode locks again -- **AND** subsequent incoming log events keep the viewport at the bottom - -### Requirement: Browser terminal history is retained in a fixed window -The management terminal SHALL retain and render at most 10,000 lines, evicting the oldest lines when new system, command, or log lines exceed that bound. Historical loading MUST use only the bounded SSE replay and MUST NOT issue a full-history request. - -#### Scenario: Buffer exceeds the retention window -- **WHEN** more than 10,000 lines have been received or generated -- **THEN** the terminal removes the oldest lines -- **AND** the newest 10,000 lines remain available for display diff --git a/openspec/changes/improve-server-terminal-log-window/tasks.md b/openspec/changes/improve-server-terminal-log-window/tasks.md deleted file mode 100644 index 708ecd9..0000000 --- a/openspec/changes/improve-server-terminal-log-window/tasks.md +++ /dev/null @@ -1,15 +0,0 @@ -## 1. Platform History Contract - -- [x] 1.1 Change the server log SSE replay to apply one capped total history budget across all streams and emit the selected tail entries in chronological order. -- [x] 1.2 Add backend tests for default/capped totals, newest-tail selection, chronological ordering, and ready-event sequencing. - -## 2. Management Terminal View - -- [ ] 2.1 Add a scroll container ref and follow-latest state to the management terminal, with initial/live bottom scrolling only while locked and unlock/relock detection at the bottom threshold. -- [x] 2.2 Increase the terminal retention buffer to 10,000 lines and keep oldest-line eviction for all incoming line types. -- [ ] 2.3 Add focused frontend tests for initial bottom positioning, scroll unlock/relock, bounded buffer behavior, and bounded SSE history options. - -## 3. Verification - -- [ ] 3.1 Run focused platform and platform_web tests, then `scripts/check-structure.sh`. -- [ ] 3.2 Run `openspec validate improve-server-terminal-log-window --strict` and record verification evidence before marking tasks complete. diff --git a/openspec/changes/integrate-real-scum-ops-workflows/.openspec.yaml b/openspec/changes/integrate-real-scum-ops-workflows/.openspec.yaml deleted file mode 100644 index d7bc011..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-10 diff --git a/openspec/changes/integrate-real-scum-ops-workflows/design.md b/openspec/changes/integrate-real-scum-ops-workflows/design.md deleted file mode 100644 index 9fee24a..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/design.md +++ /dev/null @@ -1,105 +0,0 @@ -## Context - -The repository already has pieces of the SCUM integration: log ingest and game player projections, game client bridge commands and snapshots, protected SQL/RCON/program request dispatch, source RCON input fencing, AI config diffs, gift catalogs, player state patches, remote adapter declarations, and plugin manifest validation. The current product surface still exposes broad logs/terminal/config/history concepts and several SCUM pages are placeholder-like or depend on old snapshots instead of the current server's real SCUM.db and login logs. - -The reference `scum_robot` and `scum_run` projects show the operational direction: the executor beside the game server can read SCUM.db, parse SCUM logs, query users/vehicles/flags/groups, and issue RCON commands for fame/currency. This change adapts that idea to this platform's boundaries: Platform owns intent, validation, audit, workflow records, and local projections; plugins own game-specific declarations; run/agent owns local machine execution and observed facts. The browser never receives raw SQL, host paths, DSNs, credentials, run sockets, or raw protected request text. - -## Goals / Non-Goals - -**Goals:** - -- Replace fake or placeholder SCUM data with real projections from login logs and the bound run/agent's SCUM.db observations. -- Add richer integrated operations: player profile refresh, player correction, fame/currency edits, attribute/stat mutation such as `855`, gift delivery, vehicle inventory, squad/flag governance, realtime map overlays, AI config/draft assistance, and reconciliation. -- Introduce a sequential workflow layer so multi-step SCUM operations execute predictably with dependencies, approval, safety gates, confirmation reads, idempotency, and audit evidence. -- Keep internal logs, run channels, audit, source RCON, protected request fencing, lifecycle, and AI config approval intact while removing raw product-facing panels and APIs that invite unsafe manual operations. -- Keep implementation ownership split across `platform/`, `platform_web/`, and `plugins/`; implementation work for the independent run repository must remain outside this repository. - -**Non-Goals:** - -- Do not re-add a `run/` source tree to this repository. -- Do not expose arbitrary SQL/RCON/terminal/config editing in browser APIs or plugin pages. -- Do not create billing, hosting sales, cloud provider workflows, or marketplace behavior unrelated to first-party game server management. -- Do not treat `last_save_time` as online-state proof by itself. -- Do not special-case SCUM lifecycle behavior in platform or run services; game-specific declarations stay in the SCUM plugin. - -## Decisions - -### 1. Split reads, writes, and workflows - -Realtime lists and maps use read observations. State-changing operations use typed operation declarations. Multi-step outcomes use workflows that compose observations and operations. - -Alternative considered: keep a single `protected.sql`/`database.request` escape hatch. That is too broad for recurring product features and makes it impossible to validate field-level safety, permissions, and confirmation in the UI. - -### 2. Query the current service through bound run/agent - -SCUM.db is the real source for many facts, but only the run/agent next to the current service should read it. Platform will queue server-bound observation jobs using plugin-declared templates and persist typed results into local projections. Product pages read only projections. - -Alternative considered: platform directly connects to SCUM.db through a configured path or socket. That violates run/platform ownership and leaks machine-specific execution details into control-plane code. - -### 3. Prefer RCON for game-supported writes - -Fame and currency updates follow the reference behavior using game commands such as `#SetCurrencyBalance` and `#SetFamePoints`. Database mutation is reserved for fields without a supported command path, such as declared player state/stat fields when the plugin marks them DB-only. - -Alternative considered: update all editable fields directly in SCUM.db. That increases corruption risk and bypasses the game server's own command semantics where they exist. - -### 4. Add typed mutation templates instead of browser SQL - -The SCUM plugin will declare operation templates that include operation type, schema refs, transport, target, approval level, safety rules, before-value guards, affected-row bounds, and confirmation query keys. Platform validates requests against those declarations before creating run jobs. - -Alternative considered: let admins type SQL into a protected request panel. That remains useful only as an emergency platform-admin tool, not as the backing mechanism for product workflows. - -### 5. Store projections and workflow records locally - -Platform needs local domain records for players, squads, members, vehicles, flags, current positions, observations, workflow instances, workflow steps, operation requests, and confirmations. These records power UI, API responses, stale-state visibility, idempotency, and audit. - -Alternative considered: have every page dispatch live run queries. That couples UI responsiveness to run connectivity, duplicates query logic, and creates stale-data ambiguity. - -### 6. Remove raw product surfaces, not core plumbing - -The server detail experience should drop raw logs, terminal/RCON input, raw config workbench, and generic operation history. Internal log ingest, run log channels, audit records, source RCON execution, config diff approval, and lifecycle evidence remain available to services and typed workflow status. - -Alternative considered: hide the panels but leave old APIs as-is. That keeps unsafe product paths alive and makes plugin/UI contracts harder to reason about. - -## Workflow Library - -The implementation should create these first-party workflow templates and execute them in this order as they become available: - -1. `scum.bootstrap-real-data`: verify run binding, declare SCUM.db/log sources, run schema probes, create observation cursors, and initialize stale projection records. -2. `scum.player-refresh`: parse login evidence, query SCUM.db player/economy/squad/position facts, upsert player projection, and update freshness. -3. `scum.world-refresh`: query squads, squad members, vehicles, flags, current positions, and observation checksums, then refresh map overlays. -4. `scum.player-correction`: validate player/offline state, approve typed edit, execute RCON or DB mutation, confirm by readback, and update projection. -5. `scum.gift-delivery`: evaluate schedule/eligibility, execute reward/notification, confirm result, and transition grant state. -6. `scum.territory-audit`: refresh squad/flag/member data, detect stale owner/member/flag inconsistencies, and create reviewable risk signals. -7. `scum.vehicle-audit`: refresh vehicle inventory, classify vehicle names, mark stale/missing observations, and update map inventory. -8. `scum.ai-assist`: collect allowed fields, generate reviewable config diff or workflow draft, validate, approve, dispatch, and confirm. -9. `scum.product-cleanup`: remove raw product panels/APIs and route users to typed workflow/status surfaces. - -Each workflow step records `serverInstanceId`, `workflowId`, `stepKey`, `source`, `capability`, `observedAt`, `receivedAt`, `sequence`, `checksum`, `attempt`, `status`, `safeSummary`, and audit references where applicable. - -## Risks / Trade-offs - -- [Risk] SCUM.db schema varies across game versions. → Mitigation: add version/schema probes, per-query result schemas, unknown-field handling, and plugin versioned templates. -- [Risk] SQLite reads can lock or lag during active server writes. → Mitigation: use bounded read-only execution, short timeouts, retry/backoff, and stale-state display instead of blocking product pages. -- [Risk] DB mutations can corrupt player state. → Mitigation: prefer RCON, require offline/maintenance safety windows, before-value guards, max affected rows, backup/snapshot evidence, and read-after-write confirmation. -- [Risk] Workflow retries can duplicate gifts or currency changes. → Mitigation: use idempotency keys, grant state locks, confirmation-before-retry, and unknown terminal states when execution cannot be proven. -- [Risk] Removing raw panels may hide useful diagnostics. → Mitigation: keep internal logs/audit/run evidence and expose safe workflow status summaries rather than raw terminal/log/config tools. -- [Risk] Run support lands in a separate repository. → Mitigation: define platform/plugin contracts here and track run-side implementation as an external dependency without adding run source here. - -## Migration Plan - -1. Add platform domain/repository/API contracts for observations, projections, controlled operations, workflow instances, workflow steps, and confirmations. -2. Extend SCUM plugin manifest and validator schemas for read observation templates and typed operation templates. -3. Implement platform projection services for login logs and SCUM.db typed results, then wire job completion to projection updates. -4. Implement workflow queue and first workflow templates behind server/plugin permissions. -5. Replace SCUM plugin pages with projection-backed user, squad, vehicle, flag, map, gift, AI, and workflow views. -6. Remove raw product panels and APIs after new typed workflow/status views are available. -7. Coordinate run repository changes for local SCUM.db read templates and controlled operation execution; verify browser repo contracts with stubbed or mocked run results until the external run change lands. - -Rollback keeps projections and workflow records but disables dispatch of new workflow steps through feature/config gates. Raw internal logs, audit, and lifecycle services remain untouched. - -## Open Questions - -- Exact SCUM.db field mapping for player attribute/stat field `855` must be confirmed against real server schema before declaring a production mutation template. -- Whether DB-only player state mutations require full server stop or player-offline maintenance window depends on field category and must be recorded per operation template. -- Vehicle type naming should prefer plugin/static mapping or platform-maintained trade-good mapping; this can start as unknown/fallback to entity class until verified. -- Gift item delivery transport must be finalized per reward type: companion command, RCON command, or DB mutation with confirmation. diff --git a/openspec/changes/integrate-real-scum-ops-workflows/proposal.md b/openspec/changes/integrate-real-scum-ops-workflows/proposal.md deleted file mode 100644 index f17daf2..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/proposal.md +++ /dev/null @@ -1,34 +0,0 @@ -## Why - -The SCUM plugin currently mixes placeholder surfaces, broad management tools, and incomplete real-data plumbing, so player, squad, vehicle, flag, map, gift, and state-management features cannot be trusted as live operations data. We need to turn SCUM integration into a real server-operations cockpit driven by the bound run/agent and current SCUM server facts, while preserving AI-assisted configuration and replacing unsafe/manual panels with typed workflows. - -## What Changes - -- Add real SCUM data ingestion workflows for login logs, SCUM.db read models, and platform-local projections for players, squads, vehicles, flags, positions, and observations. -- Add controlled SCUM operations for player currency, fame, attribute/stat fields such as `855`, gift delivery, notifications, and maintenance-safe data mutations. -- Add a typed workflow queue so platform can execute SCUM workflows one by one with approval, lease fencing, read-after-write confirmation, retry rules, stale-state handling, and audit evidence. -- Expand integrated SCUM features beyond the current pages: player identity enrichment, squad/flag governance, vehicle inventory, map overlays, economy and gift lifecycle, risk signals, rollback snapshots, and AI operation recommendations. -- Preserve AI assistant flows for quick plugin configuration and operations recommendations, but constrain AI output to reviewable typed diffs and allowlisted workflow requests. -- Replace product-facing log viewer, management terminal, raw config workbench, and operation-history surfaces with workflow/status views that expose only approved typed operations and safe summaries. -- **BREAKING**: Remove product-layer APIs and plugin page routes whose primary purpose is raw logs, terminal/RCON input, arbitrary config file editing, or generic operation history; internal log ingest, run channels, audit, lifecycle, source RCON, query bridge, and AI config approval remain. - -## Capabilities - -### New Capabilities - -- `scum-real-data-projections`: Real SCUM login-log and SCUM.db observation pipelines, typed query result validation, local platform projections, freshness/staleness behavior, and read-only product surfaces. -- `scum-controlled-operations`: Typed SCUM write operations using RCON where possible and bounded DB mutation only where required, with approval, offline/maintenance safety, read-after-write confirmation, and rollback evidence. -- `scum-workflow-automation`: Sequential workflow execution for SCUM operations, including workflow definitions, queueing, dependencies, status, retries, blocking states, and audit-safe progress reporting. -- `scum-product-surface`: User management, squad management, realtime map, vehicle/flag management, gift management, AI assistant integration, and removal of raw log/terminal/config/history product surfaces. - -### Modified Capabilities - -- None. - -## Impact - -- `platform/`: domain models, repositories, services, validators, DTOs, API routes, run job projection, game player/gift/state services, audit-safe workflow records, and tests. -- `platform_web/`: server detail route composition, SCUM plugin host contracts, first-party pages, API clients/types, AI assistant UI, and removal of raw product panels. -- `plugins/`: SCUM plugin manifest, schemas, query templates, operation templates, companion handlers, feature pages/contracts, SDK validation, manifest tests, and plugin validation tooling. -- External run repository: must implement the machine-side execution half for declared SCUM.db read models and controlled write operations without re-adding a `run/` tree to this repository. -- Security boundaries: browser never receives SQL text, host paths, DSNs, credentials, run sockets, or raw protected request text; failed observations never overwrite last-known-good projections. diff --git a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-controlled-operations/spec.md b/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-controlled-operations/spec.md deleted file mode 100644 index e2cd990..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-controlled-operations/spec.md +++ /dev/null @@ -1,63 +0,0 @@ -## ADDED Requirements - -### Requirement: Typed SCUM operation catalog -The system SHALL expose SCUM write capabilities only as typed operation declarations with schemas, permissions, approval level, execution transport, safety rules, and confirmation rules. - -#### Scenario: Manual player attribute edit -- **WHEN** an operator requests a player attribute/stat edit such as field `855` -- **THEN** platform creates a typed operation request containing player identity, field key, before value, after value, reason, requester, safety window, and idempotency key rather than accepting raw SQL from the browser - -#### Scenario: Operation declaration missing -- **WHEN** a plugin page, platform service, or AI assistant requests an operation type not declared by the SCUM plugin manifest and platform validator -- **THEN** platform rejects the request before any run job or protected request is created - -### Requirement: Prefer game commands over database writes -The system SHALL route SCUM write operations through game-supported commands such as RCON whenever a safe command exists, and SHALL use database mutation only for fields without a declared command path. - -#### Scenario: Fame and currency update -- **WHEN** a user requests player fame, normal currency, or gold changes -- **THEN** platform dispatches typed RCON operations using declared command templates and confirmation reads rather than issuing direct SCUM.db update statements - -#### Scenario: No game command exists -- **WHEN** a requested field is declared as database-only by the plugin operation catalog -- **THEN** platform dispatches a bounded DB mutation job with the declared template, parameter schema, row limit, before-value guard, and confirmation query - -### Requirement: Approval and permission gates -The system SHALL require role permission, operation approval, and lease-fenced run execution for all state-changing SCUM operations. - -#### Scenario: Operator requests platform-admin operation -- **WHEN** an operator without platform-admin role requests a platform-admin SCUM mutation -- **THEN** platform stores no executable payload and returns a forbidden result - -#### Scenario: Approved operation is claimed by run -- **WHEN** an approved operation creates a run job -- **THEN** run receives the executable request only through a current lease and fencing token, and platform stores only redacted or typed audit-safe payloads - -### Requirement: Safety windows for database mutation -The system SHALL require database mutations to pass configured safety checks such as player offline, maintenance verified, current-value match, bounded affected rows, and backup/snapshot evidence. - -#### Scenario: Player is online -- **WHEN** a database-only player state mutation is requested while the latest verified projection shows the player online or safety state unknown -- **THEN** platform blocks dispatch and records the request as waiting for an offline/maintenance safety window - -#### Scenario: Current value changed -- **WHEN** run attempts a DB mutation and the current DB value no longer matches the approved `before` value -- **THEN** run reports a stale-write failure and platform keeps the operation unconfirmed - -### Requirement: Read-after-write confirmation -The system SHALL mark SCUM write operations successful only after run reports execution success and platform accepts a typed confirmation observation proving the requested values now exist. - -#### Scenario: Command queued but not confirmed -- **WHEN** RCON or DB mutation dispatch succeeds but confirmation read is missing or mismatched -- **THEN** platform marks the operation pending-confirmation, failed-confirmation, or unknown instead of successful - -#### Scenario: Confirmation succeeds -- **WHEN** the confirmation read returns the expected typed values, row identity, checksum, and observedAt -- **THEN** platform marks the operation confirmed, updates local projections, and records audit evidence - -### Requirement: AI produces reviewable operations only -The system SHALL allow AI assistant output to create reviewable typed configuration diffs or typed SCUM operation drafts, but SHALL NOT allow AI to execute RCON, SQL, or file writes directly. - -#### Scenario: AI suggests a player correction -- **WHEN** AI suggests changing a player attribute, fame, currency, gift eligibility, or plugin configuration value -- **THEN** platform presents a typed diff or operation draft for human review and approval before any run-side execution can occur diff --git a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-product-surface/spec.md b/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-product-surface/spec.md deleted file mode 100644 index 104e7d0..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-product-surface/spec.md +++ /dev/null @@ -1,67 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM user management surface -The system SHALL provide a SCUM user management surface backed by local projections from login logs and SCUM.db observations. - -#### Scenario: Player list renders real records -- **WHEN** an operator opens SCUM user management -- **THEN** the page lists real player records with source, identity, character name, profile ID, Steam/user ID when known, squad, balances, fame, coordinates, online/session status evidence, and last observation freshness - -#### Scenario: Player edit opens typed workflow -- **WHEN** an operator edits fame, currency, gift eligibility, or declared player state fields -- **THEN** the page creates a typed operation workflow for review/approval instead of editing projected values directly - -### Requirement: Squad and flag management surface -The system SHALL provide squad and flag management backed by real `squad`, `squad_member`, `user_profile`, `base_element`, and related SCUM observations where available. - -#### Scenario: Squad page shows roster and territory -- **WHEN** an operator opens squad management -- **THEN** the page shows squads, members, ranks, leader, linked flags, member/player projection freshness, and unknown fields without fabricated owners or coordinates - -#### Scenario: Flag page shows stale ownership -- **WHEN** a flag observation is stale or owner data cannot be verified -- **THEN** the page marks the flag stale or unknown and offers a refresh workflow rather than inventing owner data - -### Requirement: Realtime map surface -The system SHALL provide a realtime SCUM map surface where players, vehicles, flags, squads, and selected risk overlays use platform-local projections and freshness state. - -#### Scenario: Map renders current projections -- **WHEN** map data is fresh enough for display -- **THEN** the page overlays players, vehicles, flags, squad territory, last-observed timestamps, and source confidence from local projections - -#### Scenario: Map data is stale -- **WHEN** the map has stale or missing projections -- **THEN** the page shows stale status and refresh workflow controls without using sample points, fake routes, or placeholder coordinates - -### Requirement: Gift management surface -The system SHALL provide gift catalog, schedule, eligibility, approval, delivery, notification, confirmation, and claim-state management using typed workflows. - -#### Scenario: Scheduled gift becomes eligible -- **WHEN** a daily, weekly, monthly, yearly, one-time, or multi-per-day gift rule becomes eligible for a player type or achievement condition -- **THEN** platform creates a gift delivery workflow and marks the grant delivered only after typed run/companion result and confirmation succeed - -#### Scenario: Delivery result unknown -- **WHEN** delivery dispatch succeeds but confirmation is missing or ambiguous -- **THEN** platform marks the grant unknown or pending-confirmation and prevents duplicate delivery until reconciliation completes - -### Requirement: AI assistant remains for typed configuration and workflows -The system SHALL keep the AI assistant as a first-party helper for plugin setup, config diffing, and operation drafts while routing all proposed effects through reviewable typed diffs or workflows. - -#### Scenario: AI configures plugin settings -- **WHEN** an operator asks AI to configure the SCUM plugin -- **THEN** AI can propose changes only for plugin-declared config fields, platform validates the diff, and approved changes use the existing config approval/dispatch path - -#### Scenario: AI suggests operation workflow -- **WHEN** AI suggests a player correction, gift rule, map refresh, squad audit, or vehicle audit -- **THEN** platform creates a draft workflow request that requires human review before execution - -### Requirement: Raw product surfaces removed -The system SHALL remove product-facing raw log, management terminal/RCON input, arbitrary config workbench, and generic operation history pages and dedicated APIs from the server detail experience. - -#### Scenario: Server detail route list is built -- **WHEN** platform_web builds server detail navigation and plugin workspace contracts -- **THEN** it excludes raw logs, management terminal, arbitrary config editor, and generic operation-history views while preserving AI assistant, typed workflows, lifecycle status, internal log ingest, audit, and run channels - -#### Scenario: Raw endpoint is requested -- **WHEN** a browser calls a removed product-layer raw logs, terminal, arbitrary config, or operation-history API -- **THEN** platform returns not found or the new typed workflow/status API without exposing raw terminal, raw SQL, raw host paths, or raw config editing capability diff --git a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-real-data-projections/spec.md b/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-real-data-projections/spec.md deleted file mode 100644 index 7c239d8..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-real-data-projections/spec.md +++ /dev/null @@ -1,56 +0,0 @@ -## ADDED Requirements - -### Requirement: Authentic player identity creation -The system SHALL create and update SCUM player identity records only from authentic SCUM login/logout log observations and verified SCUM.db facts reported by the bound run/agent for the current server instance. - -#### Scenario: Login log creates player record -- **WHEN** run ingests a plugin-declared SCUM `login_*.log` line for a server-bound Steam/user identifier and character name -- **THEN** platform stores or updates the matching local game player, player session, source log metadata, observed time, and confidence without inventing missing profile fields - -#### Scenario: Logout log updates session -- **WHEN** run ingests a matching logout observation for a known player session -- **THEN** platform closes or marks the local session with the observed logout time while preserving historical login evidence - -### Requirement: Bound SCUM.db read observations -The system SHALL obtain player, squad, vehicle, flag, position, economy, and observation facts from SCUM.db through server-bound run/agent jobs that execute plugin-declared read templates against the current service machine. - -#### Scenario: Current service database is queried -- **WHEN** a server instance has an active run binding with the declared SCUM SQLite read capability -- **THEN** platform queues declared read observation jobs for that server instance, run executes them against the local SCUM.db, and platform receives typed rows without exposing host paths, DSNs, sockets, or SQL text to the browser - -#### Scenario: Database unavailable -- **WHEN** the bound run reports SCUM.db missing, locked, schema-incompatible, or unavailable -- **THEN** platform records the observation failure and marks affected projections stale without replacing existing values with generated or placeholder data - -### Requirement: Local projection surfaces -The system SHALL persist local projections for players, squads, squad members, vehicles, flags, current positions, and data observations before any product surface reads them. - -#### Scenario: Typed result updates projections -- **WHEN** platform accepts a successful typed query result for a server/plugin/query binding -- **THEN** it validates schema, checksum, row bounds, observed time, and server binding before upserting the corresponding projection records - -#### Scenario: Page reads projection only -- **WHEN** platform_web or a plugin page renders SCUM users, squads, vehicles, flags, or map positions -- **THEN** it reads platform-local projections and freshness metadata rather than directly querying run, SCUM.db, or sample data - -### Requirement: Freshness and conflict handling -The system SHALL keep observation sequence, checksum, observedAt, receivedAt, and fresh/stale state for every projected SCUM data category. - -#### Scenario: Older observation arrives after newer one -- **WHEN** platform receives an observation whose sequence or observedAt is older than the current projection for the same source key -- **THEN** platform rejects it for projection while keeping an audit-safe observation record - -#### Scenario: Query failure after successful projection -- **WHEN** a later read observation fails after a prior successful projection exists -- **THEN** platform keeps the last-known-good projection, records the failure, and exposes stale status to the UI - -### Requirement: Unknown fields stay unknown -The system SHALL represent missing or unverifiable SCUM fields as unknown, absent, or stale instead of deriving fake values from unrelated tables or timestamps. - -#### Scenario: Steam ID missing from SCUM.db row -- **WHEN** a SCUM.db player row has a user_profile_id but no verified user_id/Steam identifier -- **THEN** platform stores the profile identifier separately and marks the external player identifier unknown rather than treating user_profile_id as Steam ID - -#### Scenario: last_save_time is present -- **WHEN** a prisoner row includes `last_save_time` -- **THEN** platform may use it as data freshness evidence but SHALL NOT use it alone as online-state proof diff --git a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-workflow-automation/spec.md b/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-workflow-automation/spec.md deleted file mode 100644 index 4e988a2..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/specs/scum-workflow-automation/spec.md +++ /dev/null @@ -1,68 +0,0 @@ -## ADDED Requirements - -### Requirement: Sequential workflow definitions -The system SHALL define SCUM workflows as ordered typed steps with dependencies, inputs, permissions, safety gates, execution transport, confirmation, retry policy, and blocking states. - -#### Scenario: Workflow starts with dependencies -- **WHEN** a workflow instance is created for a server instance -- **THEN** platform evaluates dependency steps and dispatches only the first runnable step whose prerequisites are satisfied - -#### Scenario: Step depends on projection freshness -- **WHEN** a step requires fresh player, squad, vehicle, flag, or position projections -- **THEN** platform runs or waits for the required observation workflow before dispatching the dependent write or UI-facing step - -### Requirement: One-by-one execution per server -The system SHALL execute SCUM workflow steps one by one per server instance when steps mutate game state, while allowing safe read observation steps to run with bounded concurrency. - -#### Scenario: Two write workflows are queued -- **WHEN** two player mutation or gift-delivery workflows target the same server instance -- **THEN** platform dispatches the next state-changing step only after the prior state-changing step reaches confirmed, failed, blocked, cancelled, or unknown terminal state - -#### Scenario: Read observation workflows are queued -- **WHEN** multiple read observation steps target players, squads, vehicles, and flags -- **THEN** platform may batch or parallelize them within declared row, timeout, and concurrency limits without violating run channel priority - -### Requirement: Integrated SCUM workflow library -The system SHALL provide first-party workflow templates for common SCUM operations that combine real data reads, typed writes, and product-state updates. - -#### Scenario: Player profile refresh workflow -- **WHEN** an operator refreshes a player profile -- **THEN** platform runs login evidence sync, player SCUM.db lookup, economy lookup, squad membership lookup, current position lookup, and projection update as one tracked workflow - -#### Scenario: Player correction workflow -- **WHEN** an operator approves a player correction -- **THEN** platform runs safety check, before-value read, RCON or DB mutation, confirmation read, projection update, and audit completion in order - -#### Scenario: Gift delivery workflow -- **WHEN** a gift grant becomes eligible by schedule, player type, achievement, or manual approval -- **THEN** platform runs eligibility check, inventory/reward operation, player notification, confirmation read/result, and grant-state transition in order - -#### Scenario: Territory risk workflow -- **WHEN** an operator opens squad or flag governance -- **THEN** platform can run squad roster refresh, flag ownership refresh, member-position overlay, stale-owner detection, and risk-signal projection without direct browser database access - -#### Scenario: Vehicle inventory workflow -- **WHEN** an operator requests vehicle inventory/map refresh -- **THEN** platform runs vehicle query, coordinate projection, owner/nearby squad enrichment where available, stale vehicle marking, and map overlay update - -#### Scenario: AI configuration workflow -- **WHEN** an operator asks AI to configure or tune the SCUM plugin -- **THEN** platform gathers allowed plugin config fields, generates a reviewable diff, validates the diff, dispatches approved writes through existing config approval, and reports completion - -### Requirement: Workflow status and audit evidence -The system SHALL expose workflow status, current step, blocker reason, retry count, confirmation status, stale data references, and audit references through safe product APIs. - -#### Scenario: Workflow blocks on missing run -- **WHEN** a workflow step requires bound run execution but no current run session can claim the capability -- **THEN** platform marks the workflow blocked with a safe reason and does not leak machine paths, tokens, sockets, or protected payload text - -#### Scenario: Workflow completes -- **WHEN** all required steps reach terminal success and confirmations pass -- **THEN** platform marks the workflow confirmed, links audit evidence, and updates the relevant player, squad, vehicle, flag, gift, or configuration projection - -### Requirement: Retry without duplicating game effects -The system SHALL apply idempotency keys, fencing tokens, and confirmation reads so retries do not duplicate gifts, duplicate currency updates, or overwrite newer player state. - -#### Scenario: Run loses connection after command dispatch -- **WHEN** run disconnects after a state-changing command may have executed -- **THEN** platform performs a confirmation read before deciding whether to retry, mark unknown, or mark confirmed diff --git a/openspec/changes/integrate-real-scum-ops-workflows/tasks.md b/openspec/changes/integrate-real-scum-ops-workflows/tasks.md deleted file mode 100644 index 285cf5b..0000000 --- a/openspec/changes/integrate-real-scum-ops-workflows/tasks.md +++ /dev/null @@ -1,101 +0,0 @@ -## 1. Workflow A - Contracts and Safety Model - -- [x] 1.1 Positive prompt: Build the shared SCUM real-data and controlled-operation contract so platform, platform_web, and plugins agree on real observation records, typed write operations, workflow instances, and confirmation states. -- [x] 1.2 Directional prompt: Update `platform/domain`, `platform/dto`, `platform/validator`, `plugins/sdk`, and manifest schemas using existing job, bridge, protected request, gift, and player-state patterns; verify with Go tests and plugin type/schema tests. -- [x] 1.3 Boundary prompt: Do not add a `run/` tree, raw SQL browser payloads, host paths, DSNs, sockets, credentials, billing, hosting sales, or unrelated SaaS marketplace behavior. -- [x] 1.4 Define domain/DTO types for SCUM observations, projection freshness, workflow instances, workflow steps, operation requests, mutation guards, confirmation results, and safe summaries. -- [x] 1.5 Extend validators to reject arbitrary SQL/RCON/terminal fields in browser/plugin payloads while accepting declared typed query and operation template keys. -- [x] 1.6 Add tests proving raw SQL, host paths, direct sockets, credentials, and undeclared operation keys are rejected before job creation. - -## 2. Workflow B - Real SCUM Data Observations - -- [x] 2.1 Positive prompt: Read real current-server SCUM facts from login logs and SCUM.db through the bound run/agent, then persist local projections without fake data. -- [x] 2.2 Directional prompt: Extend existing log ingest, game player services, remote adapter/query template plumbing, and job result projection in `platform/`; verify with unit tests around stale observations and schema validation. -- [x] 2.3 Boundary prompt: Do not query SCUM.db from platform_web, do not expose SQL text or machine paths, and do not overwrite last-known-good projections on failed observations. -- [x] 2.4 Add projection repositories for players, live states, squads, squad members, vehicles, flags, current positions, and observation metadata. -- [x] 2.5 Wire login/logout log parsing to player/session projection creation and update, preserving source evidence and unknown fields. -- [x] 2.6 Add typed SCUM.db read-result projection handlers for player profile/economy, squads, squad members, vehicles, flags, and positions. -- [x] 2.7 Add freshness/stale-state logic with sequence, observedAt, receivedAt, checksum, query key, and server/plugin binding validation. -- [x] 2.8 Add tests for older observation rejection, failed-query stale marking, user_profile_id vs Steam/user ID separation, and `last_save_time` freshness-only behavior. - -## 3. Workflow C - SCUM Plugin Read Templates - -- [x] 3.1 Positive prompt: Declare SCUM plugin read templates for users, squads, vehicles, flags, and coordinates using real SCUM.db tables and typed result schemas. -- [x] 3.2 Directional prompt: Update `plugins/examples/scum-server-plugin/manifest.json`, `schemas/bridge/queries`, plugin tests, and manifest validation while keeping query declarations safe and route-scoped. -- [x] 3.3 Boundary prompt: Do not copy scum_robot management-library fields as SCUM.db facts, do not infer missing fields, and do not let pages submit SQL text. -- [x] 3.4 Add query templates for player profile/economy/position using `user_profile`, `prisoner`, `prisoner_entity`, `entity`, `bank_account_registry`, and `bank_account_registry_currencies`. -- [x] 3.5 Add query templates for squads and members using `squad`, `squad_member`, and `user_profile`. -- [x] 3.6 Add query templates for vehicles using `vehicle_spawner` and `entity`, with unknown/fallback vehicle labels when mapping is absent. -- [x] 3.7 Add query templates for flags using `base_element`, `user_profile`, `squad_member`, and `squad` where available. -- [x] 3.8 Add schema and validation tests for row bounds, typed result shape, page/template binding, and route permissions. - -## 4. Workflow D - Controlled RCON Operations - -- [x] 4.1 Positive prompt: Support safe fame, normal currency, gold, player notification, and reward command workflows through typed RCON operations where SCUM supports commands. -- [x] 4.2 Directional prompt: Build on existing source RCON/protected RCON dispatch and game client bridge command approval paths; verify with service tests for approval, redaction, and confirmation status. -- [x] 4.3 Boundary prompt: Do not restore a product terminal or arbitrary RCON input box; do not mark queued commands as delivered or successful. -- [x] 4.4 Declare typed RCON operation templates for `player.fame.set`, `player.currency.normal.set`, `player.currency.gold.set`, `player.notify`, and command-backed reward delivery. -- [x] 4.5 Add platform services to create, approve, dispatch, and reconcile typed RCON operation requests with idempotency and audit references. -- [x] 4.6 Add read-after-write confirmation using follow-up SCUM.db observation queries or typed companion results. -- [x] 4.7 Add tests for permission denial, protected text redaction, command unknown state, confirmation failure, and duplicate prevention. - -## 5. Workflow E - Controlled DB Mutations - -- [x] 5.1 Positive prompt: Support database-only player state edits such as field `855` through typed mutation templates with maintenance/offline safety and readback confirmation. -- [x] 5.2 Directional prompt: Extend operation template schemas, platform operation services, and run job execution contracts without storing raw mutation SQL in browser-visible records; verify with stale-write and confirmation tests. -- [x] 5.3 Boundary prompt: Do not use DB mutation for fame/currency when RCON exists, do not write more than declared row bounds, and do not proceed without before-value guards. -- [x] 5.4 Add mutation template declarations for DB-only player fields with field key, table/identity mapping metadata, allowed range, safety level, confirmation query, and max affected rows. -- [x] 5.5 Add platform approval flow requiring current projection, `before` match, offline/maintenance window, backup/snapshot evidence, and platform-admin approval. -- [x] 5.6 Add run job result validation for affected rows, mutation checksum, confirmation rows, and unknown execution states. -- [x] 5.7 Add tests for online-player blocking, missing maintenance window, stale before-value, over-bound affected rows, and successful confirmation. - -## 6. Workflow F - Sequential Workflow Engine - -- [x] 6.1 Positive prompt: Create a SCUM workflow queue that can run real-data refreshes and controlled operations one by one with dependency tracking. -- [x] 6.2 Directional prompt: Add platform workflow domain/repo/service/API code near existing job/game-client/gift/player-state services; keep run execution delegated through existing job channels. -- [x] 6.3 Boundary prompt: Do not replace the generic run job scheduler, do not block control heartbeat/log/artifact channels, and do not expose protected payload text in workflow status. -- [x] 6.4 Implement workflow instance and step state transitions: draft, queued, running, waiting, blocked, confirming, confirmed, failed, unknown, cancelled. -- [x] 6.5 Implement per-server sequential dispatch for state-changing steps and bounded concurrency for read-only observation steps. -- [x] 6.6 Add workflow templates for bootstrap real data, player refresh, world refresh, player correction, gift delivery, territory audit, vehicle audit, AI assist, and product cleanup. -- [x] 6.7 Add idempotency, fencing, retry, confirmation-before-retry, and blocker-safe-summary behavior. -- [x] 6.8 Add tests for ordered execution, dependency blocking, run-unavailable blocking, retry without duplicate effects, and terminal status projection. - -## 7. Workflow G - Product APIs and Projection Views - -- [x] 7.1 Positive prompt: Expose safe SCUM APIs for projection-backed users, squads, vehicles, flags, map overlays, gifts, operations, workflows, and AI drafts. -- [x] 7.2 Directional prompt: Add `platform/api`, `platform/dto`, and service handlers following existing resource handler patterns; verify authorization tests and DTO round trips. -- [x] 7.3 Boundary prompt: Do not expose raw logs, terminal input, arbitrary config file editing, generic operation-history APIs, SQL text, DSNs, host paths, or raw protected request payloads. -- [x] 7.4 Add list/detail APIs for SCUM players, squads, squad members, vehicles, flags, current positions, map overlays, and observation freshness. -- [x] 7.5 Add workflow APIs for creating refresh/correction/gift/audit/AI workflows, listing workflow status, approving required steps, cancelling safe pending steps, and reading audit-safe summaries. -- [x] 7.6 Update or remove legacy product APIs for raw logs, management terminal, raw config workbench, and generic operation history. -- [x] 7.7 Add authorization tests for operator vs platform-admin actions and raw endpoint removal/denial. - -## 8. Workflow H - SCUM Product Surfaces - -- [x] 8.1 Positive prompt: Replace SCUM plugin pages with real projection-backed user management, squad management, realtime map, vehicle/flag management, gift management, workflow status, and AI assistant surfaces. -- [x] 8.2 Directional prompt: Update `platform_web` contracts and SCUM plugin feature pages while preserving the black mecha and crystal-moonlight theme system; run typecheck and frontend tests. -- [x] 8.3 Boundary prompt: Do not introduce generic SaaS cards, page-local fixed decoration spans, raw terminal/log/config/history panels, or fake map/sample data. -- [x] 8.4 Build user management UI showing identities, sessions, projection source, squad, fame, balances, coordinates, freshness, and typed edit workflow launchers. -- [x] 8.5 Build squad/flag UI showing rosters, ranks, leaders, flags, ownership confidence, stale state, and refresh/audit workflow controls. -- [x] 8.6 Build realtime map UI using local projections for players, vehicles, flags, squads, timestamps, stale status, and refresh controls. -- [x] 8.7 Build gift UI for catalog versions, schedules, eligibility, claims, delivery workflow status, confirmation, unknown-state reconciliation, and player notifications. -- [x] 8.8 Keep AI assistant UI for typed config diffs and workflow drafts, wired to approval flows rather than raw file editing. -- [x] 8.9 Remove raw logs, management terminal, raw config workbench, and operation history routes from server detail navigation and plugin workspace contracts. - -## 9. Workflow I - Run Integration Contract - -- [x] 9.1 Positive prompt: Define the browser-repo contract expected from the independent run repository for SCUM.db reads, RCON writes, DB mutations, confirmation reads, and log ingestion. -- [x] 9.2 Directional prompt: Document and test platform/plugin protocol expectations in this repo; keep executable run implementation for `git@git.npc0.com:admin343/run.git` outside this repository. -- [x] 9.3 Boundary prompt: Do not add run source code here, do not require deployment target/run endpoint at server creation time, and do not leak component auth keys or host paths. -- [x] 9.4 Add protocol DTOs or contract docs for read observation inputs/results, mutation inputs/results, confirmation payloads, schema probes, and safe errors. -- [x] 9.5 Add compatibility tests/mocks proving platform can process run-style read/mutation/RCON results without real run code in this repository. -- [x] 9.6 Document external run tasks needed to execute SCUM.db query templates and controlled mutation templates on the service machine. - -## 10. Workflow J - Verification, Cleanup, Commit - -- [x] 10.1 Positive prompt: Verify the full SCUM integration change with backend, frontend, plugin, structure, and OpenSpec checks before committing. -- [x] 10.2 Directional prompt: Run targeted tests during implementation and final commands: `(cd platform && go test ./...)`, `(cd platform_web && npm run typecheck && npm run test && npm run build)`, `(cd plugins && npm run typecheck && npm run test && npm run validate:manifest)`, `scripts/check-structure.sh`, and `openspec validate integrate-real-scum-ops-workflows --strict`. -- [x] 10.3 Boundary prompt: Do not mark tasks complete without verification evidence, do not stage unrelated worktree changes, and do not push if required verification or credentials fail. -- [x] 10.4 Sweep for forbidden raw SQL/RCON/terminal/config/history product surfaces and unsafe browser-visible fields. -- [x] 10.5 Update documentation or comments only where they clarify new contracts and workflow behavior. -- [x] 10.6 Stage only files changed for this task, commit on `main`, and push to the configured remote after verification succeeds. diff --git a/openspec/changes/make-run-autonomous-lifecycle-owner/.openspec.yaml b/openspec/changes/make-run-autonomous-lifecycle-owner/.openspec.yaml deleted file mode 100644 index 84cfc12..0000000 --- a/openspec/changes/make-run-autonomous-lifecycle-owner/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-06 diff --git a/openspec/changes/make-run-autonomous-lifecycle-owner/README.md b/openspec/changes/make-run-autonomous-lifecycle-owner/README.md deleted file mode 100644 index cc0b9e1..0000000 --- a/openspec/changes/make-run-autonomous-lifecycle-owner/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# make-run-autonomous-lifecycle-owner - -Make generated Run own autonomous plugin-declared lifecycle bootstrap and make Platform follow Run-reported state. diff --git a/openspec/changes/make-run-autonomous-lifecycle-owner/design.md b/openspec/changes/make-run-autonomous-lifecycle-owner/design.md deleted file mode 100644 index 5a54ad7..0000000 --- a/openspec/changes/make-run-autonomous-lifecycle-owner/design.md +++ /dev/null @@ -1,51 +0,0 @@ -## 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. -- Implement the independent Run repository's generic plan loader/executor so generated packages consume `.platform/autonomous-lifecycle-plan.json` locally and report terminal lifecycle facts back to Platform. -- Add a Platform lifecycle report endpoint that authenticates the active Run session, validates the server binding, records audit evidence, and projects visible server state from Run-reported facts without creating jobs. -- 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:** -- Re-add the independent `run` repository as a source tree owned by 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. -- **Report autonomous execution as observed facts, not job completions.** Run submits terminal autonomous lifecycle results through a signed lifecycle report route. Platform verifies the report belongs to the bound server/run session, then reuses lifecycle projection logic without manufacturing a platform job lease. -- **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 - -- **Cross-repository release skew** -> Platform can build packages with the plan before every deployed Run binary has the autonomous executor. Mitigation: generated Run logs explicit plan load/skip/failure states, tests cover the independent `run` repository, and this repo still does not own the `run/` source tree. -- **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. -- Add Platform's signed lifecycle report endpoint for Run-owned bootstrap results. -- Update the independent Run repository to load, validate, execute, and report the autonomous plan using generic lifecycle capabilities. -- 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. diff --git a/openspec/changes/make-run-autonomous-lifecycle-owner/proposal.md b/openspec/changes/make-run-autonomous-lifecycle-owner/proposal.md deleted file mode 100644 index 59a8ad5..0000000 --- a/openspec/changes/make-run-autonomous-lifecycle-owner/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## 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. diff --git a/openspec/changes/make-run-autonomous-lifecycle-owner/specs/platform-side-distribution-builds/spec.md b/openspec/changes/make-run-autonomous-lifecycle-owner/specs/platform-side-distribution-builds/spec.md deleted file mode 100644 index 4efc9cd..0000000 --- a/openspec/changes/make-run-autonomous-lifecycle-owner/specs/platform-side-distribution-builds/spec.md +++ /dev/null @@ -1,16 +0,0 @@ -## 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 diff --git a/openspec/changes/make-run-autonomous-lifecycle-owner/specs/run-autonomous-lifecycle-owner/spec.md b/openspec/changes/make-run-autonomous-lifecycle-owner/specs/run-autonomous-lifecycle-owner/spec.md deleted file mode 100644 index 8a95109..0000000 --- a/openspec/changes/make-run-autonomous-lifecycle-owner/specs/run-autonomous-lifecycle-owner/spec.md +++ /dev/null @@ -1,49 +0,0 @@ -## 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 - -#### Scenario: Generated Run reports autonomous bootstrap result -- **WHEN** Run completes an autonomous lifecycle bootstrap action from the embedded plan -- **THEN** Run reports the terminal lifecycle result to Platform without requiring a Platform job claim, acknowledgement, lease token, or job result - -### 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 - -#### Scenario: Run reports through the signed lifecycle report channel -- **WHEN** a registered Run submits a terminal lifecycle report for its bound server instance -- **THEN** Platform validates the active Run session and server binding, records audit evidence, and updates the visible server projection from the reported process facts - -### 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 diff --git a/openspec/changes/make-run-autonomous-lifecycle-owner/tasks.md b/openspec/changes/make-run-autonomous-lifecycle-owner/tasks.md deleted file mode 100644 index fe69318..0000000 --- a/openspec/changes/make-run-autonomous-lifecycle-owner/tasks.md +++ /dev/null @@ -1,29 +0,0 @@ -## 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 Add a signed Platform lifecycle report endpoint that projects server state from Run-owned terminal lifecycle facts without creating jobs. -- [x] 2.5 Update protocol and governance docs to make Run the lifecycle authority. - -## 3. Independent Run Implementation - -- [x] 3.1 Add generic autonomous lifecycle plan protocol types and validation in the independent `run` repository. -- [x] 3.2 Load `.platform/autonomous-lifecycle-plan.json` from the generated package workspace, validate package identity/target, run dependency probes/install plans, execute the bootstrap action, and report the terminal lifecycle result to Platform. -- [x] 3.3 Keep Run generic: no SCUM executable names, Steam app IDs, install paths, ports, or game-specific branches. - -## 4. Verification - -- [x] 4.1 Update service/API/runtime tests for autonomous build input, no registration-time lifecycle dispatch, Run-owned bootstrap execution, lifecycle reporting, and signed report routing. -- [x] 4.2 Run full Go tests for `platform/` and the independent `run` repository plus repository structure checks. diff --git a/openspec/changes/make-run-runtime-state-authoritative/.openspec.yaml b/openspec/changes/make-run-runtime-state-authoritative/.openspec.yaml deleted file mode 100644 index 84cfc12..0000000 --- a/openspec/changes/make-run-runtime-state-authoritative/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-06 diff --git a/openspec/changes/make-run-runtime-state-authoritative/design.md b/openspec/changes/make-run-runtime-state-authoritative/design.md deleted file mode 100644 index 150d493..0000000 --- a/openspec/changes/make-run-runtime-state-authoritative/design.md +++ /dev/null @@ -1,29 +0,0 @@ -## 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: 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:** -- 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, 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. -- 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 - -- 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 - -- 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. diff --git a/openspec/changes/make-run-runtime-state-authoritative/proposal.md b/openspec/changes/make-run-runtime-state-authoritative/proposal.md deleted file mode 100644 index 92adb62..0000000 --- a/openspec/changes/make-run-runtime-state-authoritative/proposal.md +++ /dev/null @@ -1,23 +0,0 @@ -## Why - -Manual generated Run execution exposed a stale lifecycle design: platform persisted `running` after the previous supervised process was gone, so a newly-started Run worker could register successfully but had no way to correct the visible server state before an operator issued another lifecycle command. The machine-side Run must be the source of observed runtime truth; platform state should converge from Run-reported process facts instead of blocking actions based only on stale stored state. - -## What Changes - -- 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 Platform authorization/audit for explicit operator requests while making observed runtime/process state and generated Run bootstrap Run-authoritative. - -## Capabilities - -### New Capabilities -- `run-runtime-state-authority`: Defines how platform reconciles server lifecycle state from generated Run process observations. - -### Modified Capabilities - -## Impact - -- Affects `platform/service` lifecycle projection, Run registration handling, and lifecycle tests. -- No new product areas, billing, provider workflow, or plugin-specific hardcoding. -- No new external dependencies. diff --git a/openspec/changes/make-run-runtime-state-authoritative/specs/run-runtime-state-authority/spec.md b/openspec/changes/make-run-runtime-state-authoritative/specs/run-runtime-state-authority/spec.md deleted file mode 100644 index 75103a5..0000000 --- a/openspec/changes/make-run-runtime-state-authoritative/specs/run-runtime-state-authority/spec.md +++ /dev/null @@ -1,38 +0,0 @@ -## ADDED Requirements - -### 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 waits for Run report after manual Run startup -- **WHEN** a generated Run registers for a server whose stored state is `running` -- **THEN** the platform does not enqueue a `process.status` job solely from registration - -#### 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 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`. - -#### Scenario: Run reports no managed process -- **WHEN** a status reconciliation job succeeds with `processState=not-started` -- **THEN** the platform marks the server `stopped` instead of preserving stale `running` - -#### Scenario: Run reports an unexpected exit -- **WHEN** a status reconciliation job succeeds with `processState=exited` and `exitClassification=unexpected-exit` -- **THEN** the platform marks the server `failed` - -#### Scenario: Run reports a live process -- **WHEN** a status reconciliation job succeeds with `processState=running` -- **THEN** the platform marks the server `running` - -### Requirement: Lifecycle command state changes use Run execution facts -For lifecycle start and stop jobs, the platform SHALL use Run execution facts when projecting server state. A successful `process.start` result SHALL mark `running` only when Run reports `processState=running`; a successful `process.stop` result SHALL mark `stopped` when Run reports `stopped`, `not-started`, or an operator-stopped `exited` state. - -#### Scenario: Start succeeds without a running process fact -- **WHEN** a `process.start` job succeeds but Run reports `processState=stopped` -- **THEN** the platform does not mark the server `running` - -#### Scenario: Stop succeeds from an already-stopped process -- **WHEN** a `process.stop` job succeeds with `processState=not-started` -- **THEN** the platform marks the server `stopped` diff --git a/openspec/changes/make-run-runtime-state-authoritative/tasks.md b/openspec/changes/make-run-runtime-state-authoritative/tasks.md deleted file mode 100644 index b595a8b..0000000 --- a/openspec/changes/make-run-runtime-state-authoritative/tasks.md +++ /dev/null @@ -1,9 +0,0 @@ -## 1. Runtime State Reconciliation - -- [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 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` diff --git a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/.openspec.yaml b/openspec/changes/move-scum-deployment-lifecycle-to-plugin/.openspec.yaml deleted file mode 100644 index ffa710f..0000000 --- a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-31 diff --git a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/design.md b/openspec/changes/move-scum-deployment-lifecycle-to-plugin/design.md deleted file mode 100644 index f5c52ad..0000000 --- a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/design.md +++ /dev/null @@ -1,37 +0,0 @@ -## Design - -The lifecycle boundary becomes: - -- Platform validates the selected plugin, resolves the plugin lifecycle action reference, packages plugin action assets into generated run workspaces, and dispatches a generic lifecycle job with typed inputs. -- The SCUM plugin owns Windows scripts/action specs that check whether `SCUMServer.exe` exists, install or update through SteamCMD, stop before update, write plugin-declared config values, and start with plugin-declared `port`/`MaxPlayers` plus the plugin default `-log`. -- Run executes the referenced action through generic primitives. It never branches on `game.scum`, `SCUMServer.exe`, or app `3792580`. - -### SCUM Plugin Action Model - -The SCUM install action is the authoritative setup action. It runs before first start and may also be reused as a pre-start update action. The action checks for SteamCMD, downloads it if missing, stops the SCUM process when an update is needed, runs: - -```powershell -./steamcmd.exe +force_install_dir C:/scumserver +login anonymous +app_update 3792580 validate +quit -``` - -Then the start action runs: - -```powershell -C:/scumserver\SCUM\Binaries\Win64\SCUMServer.exe -port= -MaxPlayers= -log -``` - -The concrete path may come from server deployment inputs, but the SCUM executable relative path, app id, SteamCMD arguments, and `-log` default are plugin assets, not platform/run code. - -### Platform Changes - -Platform no longer freezes a `ServerDeploymentPlan` for SCUM or requires `deployment.scum.v1`. It keeps generic deployment definitions and dispatches plugin action refs. Deployment projections can still show queued/running/failed lifecycle state from generic receipts, but detailed SCUM evidence is plugin-generated output/log/artifact data, not a platform-owned SCUM evidence schema. - -### Run Changes - -Run removes the SCUM deployment executor and SCUM capability advertisement. Assignments carrying `serverDeploymentPlan` are rejected as legacy unsupported input. Generic lifecycle template execution remains, including managed process start/stop/status, file operations, logs, and artifacts. - -## Risks / Trade-Offs - -- Existing tests expecting SCUM evidence need to shift to plugin-action dispatch assertions. -- The first plugin script implementation must be careful about Windows quoting and idempotency. -- If future games need richer setup flows, add generic action primitives or structured lifecycle DSL features without moving game policy into run. diff --git a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/proposal.md b/openspec/changes/move-scum-deployment-lifecycle-to-plugin/proposal.md deleted file mode 100644 index 43f3a7e..0000000 --- a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The current SCUM deployment flow hardcodes SteamCMD, SCUM executable paths, app id `3792580`, config writes, and start arguments inside platform/run code. That violates the intended boundary: the SCUM plugin should own game lifecycle policy while platform dispatches and run executes generic, bounded actions. - -## What Changes - -- **BREAKING**: Retire the SCUM-specific `deployment.scum.v1` run capability and the platform-to-run `serverDeploymentPlan` path for SCUM installs/adoptions. -- Move SCUM install/update/start command ownership into the SCUM plugin action bundle. -- Use plugin lifecycle actions for idempotent "install if missing, update if present, then start" behavior. -- Keep platform limited to manifest/action validation, distribution packaging, lifecycle job dispatch, and generic result projection. -- Keep run limited to generic action execution, scoped file/process operations, logs, artifacts, and capability enforcement. - -## Capabilities - -### New Capabilities - -- `plugin-owned-game-lifecycle`: Game plugins own game-specific lifecycle commands, default launch flags, app IDs, executable paths, install/update policies, and pre-start checks. - -### Modified Capabilities - -- `platform-side-distribution-builds`: Generated run packages must carry plugin-owned lifecycle action assets without requiring run to advertise game-specific deployment capabilities. - -## Impact - -- Affects `plugins/examples/scum-server-plugin` action assets and manifest declarations. -- Affects `platform/service`, `platform/domain`, `platform/dto`, `platform/validator`, and protocol docs by removing SCUM-specific deployment plan dispatch/gating. -- Affects the independent `run` repository by removing SCUM-specific runtime execution and capability advertisement. -- Requires tests proving SCUM lifecycle jobs are plugin action jobs and run no longer contains a SCUM deployment executor. diff --git a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/specs/plugin-owned-game-lifecycle/spec.md b/openspec/changes/move-scum-deployment-lifecycle-to-plugin/specs/plugin-owned-game-lifecycle/spec.md deleted file mode 100644 index 8d18bf8..0000000 --- a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/specs/plugin-owned-game-lifecycle/spec.md +++ /dev/null @@ -1,42 +0,0 @@ -# plugin-owned-game-lifecycle Specification - -## Purpose - -Ensure game-specific server lifecycle behavior lives in game plugins while platform and run remain generic. - -## ADDED Requirements - -### Requirement: Game plugins own game-specific lifecycle policy - -The system SHALL keep game-specific install, update, pre-start, start argument, stop, status, executable path, app id, and default launch flag policy in plugin-owned manifests, action specs, templates, or scripts. - -#### Scenario: SCUM lifecycle declares concrete commands in plugin assets - -- **WHEN** SCUM requires SteamCMD installation/update or server start -- **THEN** the Steam app id `3792580`, `SCUMServer.exe` relative path, `+app_update 3792580 validate`, `-port`, `-MaxPlayers`, and `-log` are provided by SCUM plugin assets or plugin startup fields -- **AND** platform and run do not hardcode those values to make SCUM lifecycle work - -### Requirement: Platform dispatches plugin lifecycle actions generically - -The platform SHALL dispatch lifecycle jobs using plugin-declared action references and generic lifecycle/deployment inputs, and SHALL NOT require a game-specific run capability for SCUM deployment. - -#### Scenario: SCUM install job is queued - -- **WHEN** a SCUM server create/install lifecycle job is created -- **THEN** the job target key references the SCUM plugin install action -- **AND** the job execution input does not include a SCUM-specific server deployment plan -- **AND** required run capabilities contain only generic lifecycle/deployment capabilities - -### Requirement: Run executes generic actions only - -Run SHALL execute lifecycle action templates and scoped process/file operations generically, and SHALL NOT branch on a game id or contain per-game deployment executors. - -#### Scenario: Legacy SCUM deployment plan reaches run - -- **WHEN** a run assignment includes a legacy `serverDeploymentPlan` -- **THEN** run rejects it as unsupported legacy input instead of executing game-specific deployment logic - -#### Scenario: Run capability report - -- **WHEN** run reports supported capabilities -- **THEN** the report does not include `deployment.scum.v1` diff --git a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/tasks.md b/openspec/changes/move-scum-deployment-lifecycle-to-plugin/tasks.md deleted file mode 100644 index 6985d75..0000000 --- a/openspec/changes/move-scum-deployment-lifecycle-to-plugin/tasks.md +++ /dev/null @@ -1,25 +0,0 @@ -## 1. OpenSpec Artifacts - -- [x] 1.1 Create proposal, design, and spec delta for plugin-owned SCUM lifecycle ownership. -- [x] 1.2 Validate the OpenSpec change strictly before completion. - -## 2. Plugin Lifecycle Assets - -- [x] 2.1 Add SCUM-owned Windows install/update and start scripts/action specs with SteamCMD app update and startup arguments owned by the plugin. -- [x] 2.2 Update plugin manifest/tests so SCUM lifecycle no longer depends on `deployment.scum.v1`. - -## 3. Platform Dispatch Boundary - -- [x] 3.1 Remove SCUM-specific deployment plan creation, capability gating, and evidence validation from platform lifecycle dispatch. -- [x] 3.2 Update platform tests/protocol docs to assert generic plugin action dispatch. - -## 4. Run Executor Boundary - -- [x] 4.1 Remove the SCUM-specific run executor/capability path and reject legacy `serverDeploymentPlan` inputs generically. -- [x] 4.2 Update run tests to prove generated run capabilities exclude `deployment.scum.v1` and generic plugin actions still execute. - -## 5. Verification And Delivery - -- [x] 5.1 Run focused plugin, platform, and run tests. -- [x] 5.2 Run `scripts/check-structure.sh` and final repository status checks. -- [x] 5.3 Commit and push main-repo and run-repo changes separately. diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/.openspec.yaml b/openspec/changes/move-scum-feature-ownership-to-plugin/.openspec.yaml deleted file mode 100644 index e8209ff..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-28 diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/design.md b/openspec/changes/move-scum-feature-ownership-to-plugin/design.md deleted file mode 100644 index e925d65..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/design.md +++ /dev/null @@ -1,33 +0,0 @@ -## Design - -The platform owns reusable authorization, tenant/server isolation, approvals, -auditing, expiry, queues, protected storage, and channels to Run. The SCUM -plugin owns its page, request generation, schemas, event parsers, and -Companion adapters. `platform_web` mounts the declared plugin page generically. - -A bridge command may declare a protected request transport of kind `sql`, -`rcon`, or `program`. The declaration names only logical transport and target -keys plus a bounded text field; it cannot name a DSN, path, socket, credential, -or executable. A plugin can generate the request text, but Platform retains it -as protected payload, emits only redacted audit metadata, and forwards it only -after the normal server scope, permission, approval, expiry, and queue checks. -Run consumes a fenced, server-bound authorized request and resolves secrets and -the actual transport locally. Platform does not parse game-specific SQL, RCON, -or program syntax. `program` means a management-program transport accepted by -Run policy, never an operating-system shell. - -Run emits SCUM process stdout/stderr console records through the durable log -channel; these are not file execution logs. The Companion parses only declared, -bounded record formats into semantic events. Unknown records make a bounded -diagnostic and are skipped. A per-server correlation digest may be derived -locally but never includes a raw network value in an upload. - -The Companion receives only authorized, server-bound bridge commands and -bounded console records. Plugins, pages, and AI never receive DSNs, paths, -credentials, raw connections, sockets, or shell access. Run results are bounded -to `succeeded`, `failed`, or `unknown` with safe diagnostics. A request failure, -unknown text, or unsupported field affects that request alone. - -Runtime capability/schema probes decide whether a particular handler is -available. They do not depend on a server/game/UE4SS/database version, build, -or source revision, and a failed probe never disables unrelated features. diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/implementation-blockers.md b/openspec/changes/move-scum-feature-ownership-to-plugin/implementation-blockers.md deleted file mode 100644 index 7a23d55..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/implementation-blockers.md +++ /dev/null @@ -1,20 +0,0 @@ -## Runtime probe evidence - -The former UE4SS reference/build/revision requirement has been removed. SCUM -features are not disabled by an update string. The Companion uses typed, -platform-authorized non-production fixtures for configuration, player-state, -reward, notification, and vehicle operations; no remote server is contacted. - -Run's required integration boundary is a bounded stdout/stderr console record -stream and declared protected SQL, RCON, or management-program transports. -Plugins generate bounded request text, while Platform authorizes, approves, -queues, redacts, and forwards it only to the bound Run request. Run alone -resolves its local transport. Paths, DSNs, credentials, raw connections, host -paths, sockets, and host OS shell access never reach the plugin, platform web, -or AI. Unknown console or request formats create a bounded diagnostic for the -affected request and no fabricated event. - -Remaining production enablement is operational: a deployed Run implementation -must provide the declared protected transports. Until then only the affected operation -is reported unavailable; the plugin page and unrelated feature capabilities -remain active. diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/ownership-audit.md b/openspec/changes/move-scum-feature-ownership-to-plugin/ownership-audit.md deleted file mode 100644 index 45d279a..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/ownership-audit.md +++ /dev/null @@ -1,21 +0,0 @@ -# SCUM transitional ownership audit - -This audit records the five transitional deliveries that placed SCUM behavior in -the platform. They are migration input only; new writes must use the declared -plugin bundle and Companion channel. - -| Transitional platform area | Existing ownership | Plugin-owned replacement | Migration dependency | -| --- | --- | --- | --- | -| `api/game_player_handlers.go`, `service/game_players.go`, `domain/game_players.go` | SCUM player profiles, sessions, risk projections | `features/players` page data projected from declared `scum.login`/`scum.logout` semantic events | Companion parser and event uploader | -| `api/game_map_trajectory_handlers.go`, `service/game_map_trajectories.go`, `domain/game_map_trajectories.go` | SCUM map conversion and trajectory projection | `features/trajectories` catalog and page projection | A verified server-side source; otherwise the page remains unavailable | -| `api/game_gift_handlers.go`, `service/game_gifts.go`, `domain/game_gifts.go` | SCUM catalog and grant workflow | `features/rewards` contracts plus declared protected request handlers | Available server-bound handler and approval | -| `api/game_player_handlers.go`, `service/game_player_state_patch.go`, `domain/game_player_state_patch.go` | SCUM field catalog and state-patch approval | `features/state-patches` declarative field catalog and protected request handler | Runtime schema availability and approval | -| `components/ScumFileConfigWorkbench.tsx` | SCUM configuration workbench | SCUM page bundle configuration catalog | Companion `config.read`/`config.patch` availability | -| `components/GamePlayerIntelligencePanel.tsx`, `GameGiftCatalogPanel.tsx`, `ScumMapTrajectoryPanel.tsx` | SCUM panels imported by the host | SCUM page bundle module | Generic manifest bundle validation and plugin-page host | -| `contracts/scumOperations.ts`, `schemas/scumOperations.ts` | `game.scum` host branch | manifest-driven bundle contract | Generic page-bundle registry | - -The generic platform primitives retained by this change are server-scoped -authorization, manifest validation, bundle identity/version validation, typed -game-client command delivery, idempotent command completion, snapshot/event -retention, audit linkage, and unavailable-feature responses. No transitional -record is treated as proof that an executable SCUM capability is available. diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/proposal.md b/openspec/changes/move-scum-feature-ownership-to-plugin/proposal.md deleted file mode 100644 index 66b1206..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/proposal.md +++ /dev/null @@ -1,33 +0,0 @@ -## Why - -SCUM plugin behavior must survive server updates without treating a game, UE4SS, -database, build, or revision string as a feature kill switch. The prior plan -also incorrectly treated plugin-generated SQL and management-command text as a -direct-access surface. Generating text is distinct from possessing a DSN, RCON -credential, host path, socket, or shell capability. - -## What Changes - -- Move all SCUM feature authority to the plugin and its Companion, with generic - platform authorization, isolation, approval, audit, expiry, queue, protected - storage, and Run channels. -- Replace build/version/revision gates with runtime schema and capability probes. -- Let plugins declare and generate bounded SQL, RCON, or program-management - request text for a logical, server-bound protected transport. Platform stores, - authorizes, approves, audits, expires, and forwards that opaque payload; Run - alone resolves the bound transport and executes the authorized request. -- Keep platform transport-agnostic: it validates declarations, scope, limits, - lifecycle, and redaction but does not parse SCUM SQL, RCON, or program syntax. -- Let Run provide bounded SCUM process stdout/stderr console records through the - platform log channel for plugin parsing. These are not file execution logs. -- Require bounded `succeeded`/`failed`/`unknown` result classifications and safe - diagnostics. Unknown text, command formats, and fields affect only the one - request and never disable unrelated features. - -## Non-Goals - -No plugin, page, AI request, or result projection receives a DSN, database -path, raw connection, RCON credential, host path, direct socket, or shell. -Protected program-management requests are not host OS shell requests. No OCR, -screenshot, keyboard/mouse injection, desktop automation, Run source, SCUM -import, or `game.scum` branch is added to `platform_web`. diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/specs/scum-companion-runtime-adapter/spec.md b/openspec/changes/move-scum-feature-ownership-to-plugin/specs/scum-companion-runtime-adapter/spec.md deleted file mode 100644 index 490ab7d..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/specs/scum-companion-runtime-adapter/spec.md +++ /dev/null @@ -1,45 +0,0 @@ -## ADDED Requirements - -### Requirement: Companion uses runtime capability isolation - -The SCUM Companion SHALL dispatch only declared typed handlers bound to its -authorized server. Handler availability SHALL come from runtime capability and -schema probes, not a game, UE4SS, database, build, revision, or version gate. -A probe or command failure SHALL affect only that handler invocation. - -#### Scenario: A runtime adapter is unavailable - -- **WHEN** a typed port or schema probe is unavailable -- **THEN** the Companion returns a typed unavailable/failed/unknown result for - that command and does not disable an unrelated plugin feature - -### Requirement: Protected requests are platform mediated - -The SCUM plugin SHALL be able to generate bounded SQL, RCON, or -program-management request text for a declared logical protected transport. -Platform SHALL authorize, isolate by tenant and server, approve, audit with -redaction, expire, queue, store, and forward each opaque request to the bound -Run endpoint. Platform SHALL not parse SCUM SQL, RCON, or program syntax. Run -SHALL execute only a current, authorized, server-bound request and return a -bounded `succeeded`, `failed`, or `unknown` result with safe diagnostics. -No plugin, web page, or AI request SHALL receive a path, DSN, raw connection, -credential, host path, direct socket, or shell capability. - -#### Scenario: Unsupported request format - -- **WHEN** Run cannot recognize a request text, command format, or field -- **THEN** it returns `failed` or `unknown` with a safe diagnostic for that - request and does not disable an unrelated capability - -### Requirement: SCUM console records use the log channel - -Run SHALL send bounded SCUM process stdout/stderr console records through the -durable platform log channel. The Companion SHALL parse only declared bounded -formats and skip unknown lines with a bounded diagnostic. Console records are -not file execution logs. - -#### Scenario: Unknown console output - -- **WHEN** stdout or stderr does not match a declared semantic parser -- **THEN** the Companion records a bounded diagnostic and uploads no semantic - event or raw line diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/specs/scum-plugin-feature-ownership/spec.md b/openspec/changes/move-scum-feature-ownership-to-plugin/specs/scum-plugin-feature-ownership/spec.md deleted file mode 100644 index b702cb8..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/specs/scum-plugin-feature-ownership/spec.md +++ /dev/null @@ -1,53 +0,0 @@ -## ADDED Requirements - -### Requirement: SCUM feature ownership is plugin-local - -The SCUM plugin SHALL own SCUM schemas, allowlists, migration adapters, -Companion behavior, and feature UI. The platform SHALL retain only reusable -authorization, isolation, approval, auditing, expiry, queues, protected -storage, generic Run transport declarations, and generic plugin-host -primitives. `platform_web` SHALL not import SCUM code or branch on `game.scum`. - -#### Scenario: Page mounting - -- **WHEN** an authorized administrator opens the installed plugin route -- **THEN** the generic host mounts the declared plugin bundle with only - server-scoped permission context - -### Requirement: Feature availability is runtime scoped - -The plugin SHALL expose a feature as actionable only when its declared -Companion handler or event producer is currently available for that server. -Availability SHALL not be gated by a game or adapter version/build/revision. - -#### Scenario: One adapter fails - -- **WHEN** a schema probe for state patching fails -- **THEN** state patching is unavailable with a typed reason while other - declared capabilities remain independently available - -### Requirement: Transitional records are read-only migration input - -Platform records MAY be displayed with provenance while plugin-owned records -become authoritative per server and feature. Migration flags SHALL be scoped -to the server and feature, never to a game version. - -#### Scenario: Migration flag is absent - -- **WHEN** no unique server-feature migration flag is present -- **THEN** historical records remain readable and plugin writes stay disabled - -### Requirement: Protected request declarations are generic - -The plugin manifest and SDK SHALL support generic declared protected request -transports for SQL, RCON, and management-program text. Declarations SHALL use -only logical server-bound transport/target keys and bounded text fields. -Browser projections and audit records SHALL redact request text. Declarations -shall not grant credentials, paths, raw connections, direct sockets, or host OS -shell execution. - -#### Scenario: Plugin generates an SQL request - -- **WHEN** the plugin queues SQL text through a declared protected transport -- **THEN** Platform stores and audits only its protected/redacted form and - forwards it only after generic authorization and approval checks diff --git a/openspec/changes/move-scum-feature-ownership-to-plugin/tasks.md b/openspec/changes/move-scum-feature-ownership-to-plugin/tasks.md deleted file mode 100644 index 7f30580..0000000 --- a/openspec/changes/move-scum-feature-ownership-to-plugin/tasks.md +++ /dev/null @@ -1,17 +0,0 @@ -## 1. Reopen the architecture boundary - -- [x] 1.1 Replace the prior SQL/RCON prohibition and fixed SCUM command template in proposal, design, and specifications with generic protected request semantics. -- [x] 1.2 Preserve runtime probe isolation while removing every SCUM/UE4SS/database build, revision, and version gate. - -## 2. Define browser-side protected request contracts - -- [x] 2.1 Add generic SQL, RCON, and management-program protected transport declarations to the manifest schema, platform domain validation, and plugin SDK. -- [x] 2.2 Permit only a declared bounded request-text field for protected commands; continue rejecting credentials, DSNs, paths, raw connections, direct sockets, and host OS shell material. -- [x] 2.3 Keep queue, approval, server/tenant isolation, expiry, and Run-facing protected payload semantics generic; redact text from browser responses and audit summaries. -- [x] 2.4 Declare SCUM plugin protected database and management transports without adding SCUM parsing or a fixed command template. - -## 3. Verify and deliver - -- [x] 3.1 Update focused Go and TypeScript tests for declarations, request generation, redaction, and safe rejection. -- [x] 3.2 Run focused Go/TS tests, OpenSpec strict validation, and structure verification. -- [x] 3.3 Forward approved protected bridge requests through a signed, fenced, one-time Platform→Run input route; verify redaction, server/transport binding, terminal result projection, commit, and push `main`. diff --git a/openspec/changes/persist-runtime-profiles-and-server-bindings/.openspec.yaml b/openspec/changes/persist-runtime-profiles-and-server-bindings/.openspec.yaml deleted file mode 100644 index ff5f854..0000000 --- a/openspec/changes/persist-runtime-profiles-and-server-bindings/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-17 diff --git a/openspec/changes/persist-runtime-profiles-and-server-bindings/design.md b/openspec/changes/persist-runtime-profiles-and-server-bindings/design.md deleted file mode 100644 index 6af6c1a..0000000 --- a/openspec/changes/persist-runtime-profiles-and-server-bindings/design.md +++ /dev/null @@ -1,75 +0,0 @@ -## Context - -The plugin JSON schema and TypeScript SDK already define discovery probes, lifecycle profiles, dependency probes/install plans, log sources, transports, and client-manager profiles. Platform manifest DTOs do not decode `runtimeProfiles`, `GamePlugin` does not store them, and durable snapshots omit `RuntimeBinding`, so those declarations and bindings disappear before they can drive a server workflow. The existing action gate also reports complete when no binding rows exist. - -The platform uses typed domain/model records behind repository interfaces. File and MySQL backends durably serialize the same `StoreSnapshot`, while tests use `MemoryStore`. The web console consumes platform-owned safe DTOs and must never receive raw host paths, direct sockets, credentials, or secret storage values. - -## Goals / Non-Goals - -**Goals:** - -- Preserve every supported safe runtime-profile declaration during manifest registration and durable reload. -- Select exactly one declared lifecycle profile per server runtime binding and derive its required logical keys deterministically. -- Create or update bindings through owner/admin-authorized APIs, persist them in every durable store, and expose only redacted readiness metadata. -- Require a complete binding before lifecycle and runtime actions whose execution depends on the selected profile. -- Make profile selection and binding completion available in the create-server and server-detail workflows. - -**Non-Goals:** - -- Storing raw credentials, direct sockets, or host filesystem paths in platform metadata. -- Implementing a general secret vault, run-side path resolver, durable scheduler, process supervisor, log/artifact backend, dependency installer, self-update system, or client-manager deployment lifecycle. -- Changing the independent run repository or declaring the wider production-readiness roadmap complete. - -## Decisions - -### Decision 1: Persist typed profiles on the installed plugin record - -Platform will mirror the existing manifest/SDK runtime profile structures in domain, DTO, and model packages and copy them into `GamePlugin` at registration. File/MySQL snapshots already persist plugin records, so this preserves the immutable installed-version contract without reaching across repository roots or storing arbitrary manifest JSON. - -Alternative considered: keep only the manifest artifact reference and re-read the artifact for every request. This was rejected because artifact availability is a separate lifecycle, it makes validation/reload behavior non-deterministic, and it leaves action gating dependent on external content. - -### Decision 2: One server-scoped binding aggregate selects one lifecycle profile - -Each server has one `RuntimeBinding` identified deterministically from its server ID. It records the plugin ID/version contract, selected lifecycle `profileKey`, profile mode, logical binding refs, derived missing keys, and readiness status. Required keys are derived from the selected lifecycle profile and the referenced discovery, dependency, log, transport, and client-manager declarations; callers cannot self-assert `complete` or `missingKeys`. - -Alternative considered: one row per logical key. This was rejected for now because profile changes need atomic validation and readiness projection, while the existing repository abstraction has aggregate create/update semantics. - -### Decision 3: Accept safe opaque values, return redacted metadata - -Write requests accept logical references and `secret://` references only. Values containing raw absolute paths, URI sockets/DSNs, inline credentials, traversal, or other unsafe material are rejected. Read responses return each logical key with a `configured` boolean and `secret` boolean, never the stored value or internal secret-storage location. Missing reasons name only declared logical keys. - -Alternative considered: return stored logical refs directly. This was rejected because even non-secret refs can encode topology or storage details and the browser does not need them to review readiness. - -### Decision 4: Create workflow persists the binding before dispatch - -Server creation requires a declared profile key and optional initial binding values. The service validates the plugin/profile, creates the server and its binding, verifies completeness, and only then queues install. If required keys are missing, the request fails with logical missing-key details and no install job is dispatched. Repository rollback is limited by the current non-transactional abstraction; validation is therefore completed before the first write, and a binding persistence failure prevents dispatch and is surfaced explicitly. - -Existing stored servers without a binding remain readable but are action-gated with a safe `runtime profile is not configured` reason until an owner or platform admin configures one. - -### Decision 5: Authorization reuses server ownership rules - -Listing a binding uses server visibility; changing it requires server ownership or platform-admin authority. Plugin pages receive no direct binding mutation surface. Lifecycle services independently check binding readiness so bypassing the UI or runtime-action projection cannot dispatch work. - -### Decision 6: Web forms use declared contract data - -`GamePluginResponse` exposes safe runtime-profile declarations required for selection and labels. The create form renders the selected plugin's lifecycle profiles and declared logical keys, submits the real profile and bindings, and avoids path/socket/credential terminology. The server detail view loads the redacted binding, supports profile changes and logical-key updates, and shows safe missing reasons. - -## Risks / Trade-offs - -- [Snapshot writes are aggregate and not transactional across server and binding repositories] -> Perform all validation before writes, persist the binding before job dispatch, and add failure/reload tests; a later durable-job change can introduce transactions. -- [Profile key derivation can over-require unrelated declarations] -> Scope derivation to the selected lifecycle profile and directly referenced transports/client manager, plus required global discovery/dependency/log targets. -- [Opaque safe refs cannot prove run-side resolvability] -> Treat platform completeness as contract completeness only; run-side resolution/health remains a later lifecycle responsibility. -- [Existing servers become gated after upgrade] -> Keep them readable and return a safe configuration-required reason; operators can select a profile in server detail. -- [Changing an active server profile could invalidate running work] -> Reject binding updates while the server is installing or running; require a stable non-active state. - -## Migration Plan - -1. Deploy profile-aware decoding and snapshot fields with backward-compatible empty defaults. -2. Existing plugin records without persisted profiles remain listable but cannot configure a runtime binding until the manifest is re-registered. -3. Existing servers without bindings remain visible with lifecycle/runtime actions disabled. -4. Re-register manifests, then configure each server binding through the authorized detail workflow. -5. Rollback can ignore the additive JSON fields; no raw secret values are introduced by this change. - -## Open Questions - -- Transactional multi-resource creation and encrypted secret material persistence are deferred to the next security/persistence task rather than being represented as complete here. diff --git a/openspec/changes/persist-runtime-profiles-and-server-bindings/proposal.md b/openspec/changes/persist-runtime-profiles-and-server-bindings/proposal.md deleted file mode 100644 index bdf5e66..0000000 --- a/openspec/changes/persist-runtime-profiles-and-server-bindings/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -Plugin manifests already describe runtime profiles, but platform registration discards those declarations and runtime bindings live only in an in-memory repository that is absent from durable snapshots. As a result, server creation cannot select a real profile or persist its logical bindings, and action gating incorrectly treats a server with no bindings as complete. - -## What Changes - -- Persist the complete safe runtime-profile contract from plugin manifest registration through domain, DTO, model, repositories, and durable file/MySQL snapshots. -- Add authorized server runtime-binding APIs and service operations for listing and updating one selected profile with validated logical values or secret references. -- Extend server creation to select a declared lifecycle profile and submit its initial logical bindings atomically with the instance workflow. -- Gate lifecycle and distribution actions on the selected profile and its required binding keys, returning only safe logical missing reasons. -- Add server creation and detail UI for choosing, reviewing, completing, and changing runtime bindings without displaying raw host paths, sockets, credentials, or secret storage details. -- Add plugin, platform, persistence, API, and frontend regression coverage for manifest projection, reload durability, invalid/missing binding rejection, action gating, and non-disclosure. - -## Capabilities - -### New Capabilities - -- `runtime-profile-bindings`: Persist plugin-declared runtime profiles and provide server-scoped profile selection, logical binding management, secure projections, and action readiness. - -### Modified Capabilities - - -## Impact - -- `plugins/`: manifest/SDK validation and fixtures remain the source contract and gain persistence-oriented regression coverage where needed. -- `platform/`: runtime profile domain/DTO/model validation, store snapshots, server lifecycle creation, binding services/routes, authorization, and action gating. -- `platform_web/`: API contracts, create-server form, server-detail binding workflow, and focused tests. -- Public platform API requests and responses gain runtime profile and binding fields/routes; no raw machine location or credential data crosses into the web or plugin page boundary. diff --git a/openspec/changes/persist-runtime-profiles-and-server-bindings/specs/runtime-profile-bindings/spec.md b/openspec/changes/persist-runtime-profiles-and-server-bindings/specs/runtime-profile-bindings/spec.md deleted file mode 100644 index 8216cdb..0000000 --- a/openspec/changes/persist-runtime-profiles-and-server-bindings/specs/runtime-profile-bindings/spec.md +++ /dev/null @@ -1,86 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform persists plugin runtime profiles -The platform SHALL decode, validate, store, and return safe plugin runtime profiles covering server discovery, lifecycle, dependencies and install plans, log sources, transports, and client-manager declarations. - -#### Scenario: Manifest registration survives reload -- **WHEN** an operator registers a valid plugin manifest with runtime profiles and the durable store is reopened -- **THEN** the installed plugin retains the same validated runtime-profile contract - -#### Scenario: Unsafe runtime declaration is rejected -- **WHEN** a manifest runtime profile includes a raw host path, direct socket, credential, secret value, or arbitrary shell content -- **THEN** registration fails without persisting the unsafe declaration - -### Requirement: Server selects a declared runtime profile -Each server runtime binding SHALL select a lifecycle profile declared by its installed plugin and SHALL derive required logical keys from that profile and its referenced runtime declarations. - -#### Scenario: Valid profile selection -- **WHEN** an authorized operator selects a declared lifecycle profile for a server -- **THEN** the platform stores the server, plugin, profile, mode, derived required keys, and readiness state - -#### Scenario: Undeclared profile is rejected -- **WHEN** a caller selects a profile key or logical binding key not declared by the server's plugin -- **THEN** the platform rejects the request without changing the stored binding - -### Requirement: Runtime bindings are durable and authorized -The platform SHALL persist runtime bindings in memory, file, and MySQL-backed repository contracts and SHALL authorize server-scoped reads and owner/admin-scoped changes. - -#### Scenario: Binding survives durable reload -- **WHEN** a valid binding is written through a durable store and the store is reopened -- **THEN** the selected profile and readiness metadata remain available for that server - -#### Scenario: Unauthorized binding update -- **WHEN** a user who is neither platform admin nor server owner attempts to change a server binding -- **THEN** the platform denies the update and leaves the binding unchanged - -### Requirement: Binding projections do not disclose runtime details -Runtime binding responses SHALL expose only profile metadata, declared logical keys, configured/secret flags, missing keys, status, and timestamps; they MUST NOT expose stored values, raw host paths, direct sockets, credentials, DSNs, or internal secret-storage locations. - -#### Scenario: Secret reference is configured -- **WHEN** a stored logical binding uses a secret reference -- **THEN** the API reports that the logical key is configured and secret-backed without returning the reference value - -#### Scenario: Missing logical binding is reviewed -- **WHEN** a required logical key is absent -- **THEN** the API and web console display the logical key and a safe configuration reason without storage details - -### Requirement: Actions require a complete runtime binding -The platform SHALL gate lifecycle and runtime-dependent actions on the presence of a valid, complete runtime binding for the server's current plugin and selected profile. - -#### Scenario: No binding is not complete -- **WHEN** a server has no runtime binding -- **THEN** lifecycle and runtime-dependent actions are disabled or rejected with a safe profile-not-configured reason - -#### Scenario: Missing binding blocks dispatch -- **WHEN** a selected profile has one or more missing required logical keys -- **THEN** the platform does not dispatch the requested action and reports only the missing logical keys - -#### Scenario: Complete binding permits normal validation -- **WHEN** the selected profile has all required logical keys configured -- **THEN** action handling proceeds to existing permission, endpoint capability, state, and idempotency checks - -### Requirement: Server creation submits a real profile and bindings -The server creation workflow SHALL require a declared runtime profile and SHALL persist validated initial bindings before dispatching the install job. - -#### Scenario: Complete create request -- **WHEN** an operator submits a server, declared profile, and all required logical bindings -- **THEN** the platform persists the server and binding and queues the install job with the selected profile context - -#### Scenario: Incomplete create request -- **WHEN** a create request omits a required logical binding -- **THEN** the platform rejects creation before dispatch and identifies only the missing logical key - -### Requirement: Operators can review and amend bindings -The management console SHALL derive profile choices and logical binding inputs from plugin declarations and SHALL provide a server-detail workflow to review or amend the selected profile and binding completeness. - -#### Scenario: Create form submits selected contract -- **WHEN** an operator selects a plugin and profile and completes declared logical fields -- **THEN** the web client submits the actual profile key and binding map in the create workflow request - -#### Scenario: Detail workflow updates bindings safely -- **WHEN** an authorized operator changes a stopped or draft server's profile or logical bindings -- **THEN** the console saves through the runtime-binding API and refreshes the redacted readiness projection - -#### Scenario: UI does not render sensitive runtime data -- **WHEN** a binding response is rendered in create or detail workflows -- **THEN** the UI contains no secret value, raw path, socket, DSN, or internal storage reference diff --git a/openspec/changes/persist-runtime-profiles-and-server-bindings/tasks.md b/openspec/changes/persist-runtime-profiles-and-server-bindings/tasks.md deleted file mode 100644 index 8ee3773..0000000 --- a/openspec/changes/persist-runtime-profiles-and-server-bindings/tasks.md +++ /dev/null @@ -1,32 +0,0 @@ -## 1. Runtime Profile Contract - -- [x] 1.1 Add typed runtime profile declarations to platform domain, DTO, model, copy, and response projections. -- [x] 1.2 Validate profile keys, references, capabilities, safe strings, and cross-profile references during manifest registration. -- [x] 1.3 Persist registered runtime profiles through file/MySQL snapshots and prove reload behavior with tests. - -## 2. Runtime Binding Persistence and API - -- [x] 2.1 Add runtime binding snapshot persistence and repository reload coverage. -- [x] 2.2 Implement required logical-key derivation and binding validation against the selected plugin profile. -- [x] 2.3 Implement authorized list/update binding services and redacted DTO projections. -- [x] 2.4 Add documented server runtime-binding routes and API authorization/non-disclosure tests. - -## 3. Lifecycle and Action Gating - -- [x] 3.1 Extend server creation DTO/domain flow with a required profile key and initial logical bindings. -- [x] 3.2 Persist the binding before install dispatch and reject incomplete or undeclared create inputs. -- [x] 3.3 Gate start/stop and runtime-dependent actions on a present, current, complete binding with safe reasons. -- [x] 3.4 Add service/API regressions for missing/invalid bindings, successful dispatch, and reload survival. - -## 4. Management Console - -- [x] 4.1 Add frontend API types/client methods and form contracts for runtime profiles and redacted bindings. -- [x] 4.2 Connect plugin profile selection and declared logical binding inputs to the create-server workflow. -- [x] 4.3 Add a server-detail review/update workflow with safe missing reasons and no runtime value disclosure. -- [x] 4.4 Add frontend regressions proving real request submission, review/update behavior, and secret/path/socket non-disclosure. - -## 5. Verification and Documentation - -- [x] 5.1 Update platform, plugin, and web API/domain documentation for persisted profile and binding behavior without claiming later roadmap readiness. -- [x] 5.2 Run plugin manifest/SDK tests, platform Go tests, platform_web tests/typecheck/build, and risk-relevant run tests if run changes are required. -- [x] 5.3 Run `openspec validate persist-runtime-profiles-and-server-bindings --strict` and `scripts/check-structure.sh`, then record only evidence-backed completion. diff --git a/openspec/changes/polish-ai-provider-onboarding/proposal.md b/openspec/changes/polish-ai-provider-onboarding/proposal.md deleted file mode 100644 index 316f571..0000000 --- a/openspec/changes/polish-ai-provider-onboarding/proposal.md +++ /dev/null @@ -1,19 +0,0 @@ -## Why - -AI provider setup currently exposes implementation fields such as provider ID, Base URL, models, timeout, and redaction policy as the primary workflow. This is too noisy for normal provider onboarding and makes operators fill fields that the platform can derive from a provider preset. The magical-girl theme also becomes visually harsh when a user-uploaded background is active because the custom-background overrides reintroduce high-saturation pink/gold overlays on already busy imagery. - -## What Changes - -- Simplify AI provider onboarding so the normal path is provider kind plus platform secret reference, with provider ID generated automatically. -- Move Base URL, model list, default model, relay mode, timeout, and redaction policy into an advanced section with provider-specific defaults. -- Preserve the platform boundary: the frontend manages secret references and does not persist or display raw API keys. -- Require AI provider management API routes to use platform administrator authentication. -- Reduce magical-girl custom-background surface saturation, frame accessory opacity, and pink/gold glow while keeping the crystal-moonlight visual direction. -- Document the local debug port discipline: use `scripts/local-debug-start.sh` and the documented `LOCAL_DEBUG_*` overrides instead of starting ad hoc ports. - -## Impact - -- Affected roots: `platform/`, `platform_web/`, `docs/`, `openspec/`. -- Expected files: AI provider API handlers/tests, AI provider frontend contracts/schema/page/tests, shared theme CSS/tests/readme, local-debug docs. -- No billing, cloud host sales, provider marketplace, direct run socket, plugin credential, or unrelated SaaS behavior changes. -- Verification: focused backend/frontend tests, `scripts/check-structure.sh`, `openspec validate polish-ai-provider-onboarding --strict`, and browser walkthrough against the existing local debug stack. diff --git a/openspec/changes/polish-ai-provider-onboarding/specs/ai-provider-management/spec.md b/openspec/changes/polish-ai-provider-onboarding/specs/ai-provider-management/spec.md deleted file mode 100644 index 91cd7af..0000000 --- a/openspec/changes/polish-ai-provider-onboarding/specs/ai-provider-management/spec.md +++ /dev/null @@ -1,35 +0,0 @@ -## MODIFIED Requirements - -### Requirement: AI provider management preserves secret boundaries -AI provider management SHALL reject raw key material in request fields and SHALL never expose raw API keys in API responses or frontend-visible types. - -#### Scenario: Raw key is submitted during update -- **WHEN** a create or update request includes raw key material instead of a secret reference in `apiKeyRef` -- **THEN** the platform MUST reject the request with a validation error and MUST NOT persist the provider - -#### Scenario: Provider is returned to UI -- **WHEN** the backend or frontend API client returns provider data -- **THEN** the response/type MUST include `apiKeyRef` only and MUST NOT include `apiKey`, `rawApiKey`, or equivalent raw credential fields - -#### Scenario: Provider management route is accessed without platform admin -- **WHEN** a client creates, lists, reads, updates, tests, changes status, or lists models for AI providers without a platform administrator bearer session -- **THEN** the platform MUST reject the request with a stable JSON authorization error - -### Requirement: AI provider console page is functional -The management console SHALL provide a functional operational view for configured providers while keeping normal setup focused on provider kind and platform-owned secret references. - -#### Scenario: Operator opens AI provider page -- **WHEN** the AI provider page renders -- **THEN** it MUST show provider counts, status distribution, configured model counts, and a provider table - -#### Scenario: Operator creates provider from normal form -- **WHEN** an operator creates a provider through the normal form -- **THEN** the page MUST generate the provider ID and apply provider-specific defaults for Base URL, model list, relay mode, timeout, and redaction policy before submitting named API requests - -#### Scenario: Operator edits advanced provider metadata -- **WHEN** an operator opens advanced settings -- **THEN** the page MAY allow editing Base URL, model list, default model, relay mode, timeout, and redaction policy without requiring the operator to manually edit the provider ID - -#### Scenario: Operator uses provider actions -- **WHEN** an operator triggers enable/disable, test, or model-list actions -- **THEN** the page MUST call the matching API client methods and display the redacted result state diff --git a/openspec/changes/polish-ai-provider-onboarding/specs/platform-web-theme/spec.md b/openspec/changes/polish-ai-provider-onboarding/specs/platform-web-theme/spec.md deleted file mode 100644 index 845c6ca..0000000 --- a/openspec/changes/polish-ai-provider-onboarding/specs/platform-web-theme/spec.md +++ /dev/null @@ -1,12 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Uploaded backgrounds remain readable and restrained -The platform_web theme system SHALL preserve uploaded background visibility while keeping operational surfaces readable and avoiding harsh high-saturation overlays. - -#### Scenario: Magical-girl theme uses a custom background -- **WHEN** `data-custom-background="true"` and `data-theme-palette="magical-girl"` are active -- **THEN** shared operational surfaces MUST use restrained translucent materials, muted frame accessories, and reduced glow so the background does not become visually harsh - -#### Scenario: Custom background theme styling changes -- **WHEN** custom-background shared CSS is modified -- **THEN** CSS contract tests or theme documentation MUST cover the intended restraint so future changes do not reintroduce excessive pink/gold gradients diff --git a/openspec/changes/polish-ai-provider-onboarding/tasks.md b/openspec/changes/polish-ai-provider-onboarding/tasks.md deleted file mode 100644 index a674e1a..0000000 --- a/openspec/changes/polish-ai-provider-onboarding/tasks.md +++ /dev/null @@ -1,39 +0,0 @@ -## 1. AI Provider Setup - -- [x] 1.1 Hide provider ID from the primary form and generate it deterministically from provider kind/name. -- [x] 1.2 Keep the primary workflow to provider kind plus platform secret reference, using official-provider defaults for Base URL, models, relay mode, timeout, and redaction policy. -- [x] 1.3 Move lower-frequency provider metadata into an advanced section. -- [x] 1.4 Preserve frontend and backend raw-key redaction boundaries. - -## 2. API Authorization - -- [x] 2.1 Require platform administrator authentication for AI provider create/list/detail/update/status/test/models routes. -- [x] 2.2 Add or update backend tests for authorized management and unauthorized rejection. - -## 3. Custom Background Theme Polish - -- [x] 3.1 Tone down magical-girl custom-background panel gradients, frame ornaments, and glow. -- [x] 3.2 Add CSS contract coverage so custom-background magical-girl overrides stay muted. -- [x] 3.3 Update theme documentation to explain custom-background restraint. - -## 4. Local Debug Documentation - -- [x] 4.1 Document that agents should use `scripts/local-debug-start.sh` and existing default ports unless explicit `LOCAL_DEBUG_*` overrides are provided. -- [x] 4.2 Document restart discipline through `scripts/local-debug-stop.sh` then `scripts/local-debug-start.sh`. - -## 5. Verification - -- [x] 5.1 Run focused backend AI provider API tests. -- [x] 5.2 Run focused frontend AI provider/theme tests plus typecheck/build if touched code requires it. -- [x] 5.3 Run `scripts/check-structure.sh`. -- [x] 5.4 Run `openspec validate polish-ai-provider-onboarding --strict`. -- [x] 5.5 Browser-walkthrough `http://127.0.0.1:5173/#/aiProviders` using the existing local debug stack; cover magical-girl custom-background via CSS contract test. - -## Evidence - -- `npm test -- AiProvidersPage.test.tsx aiProviders.test.ts base-css.test.js` passed. -- `go test ./api -run 'TestAIProvider|TestAIInvocation|TestPluginBridgeExecute|TestCoreAPI(CreateListDetailWorkflows|ErrorResponses)' -count=1` passed. -- `npm run typecheck` and `npm run build` passed in `platform_web`. -- `scripts/check-structure.sh` passed. -- `openspec validate polish-ai-provider-onboarding --strict` returned valid; PostHog telemetry flush failed due DNS after validation success. -- Browser walkthrough used existing `http://127.0.0.1:5173/#/aiProviders`: logged in with local debug account, opened 新增提供商, confirmed no editable ID input, generated ID copy, and no visible raw key copy. OpenAI defaults `gpt-5.6-terra, gpt-5.6-luna` are covered by `schemas/aiProviders.test.ts`; magical-girl custom-background restraint is covered by `theme/base-css.test.js`. diff --git a/openspec/changes/polish-platform-interaction-design/.openspec.yaml b/openspec/changes/polish-platform-interaction-design/.openspec.yaml deleted file mode 100644 index 8cceb8d..0000000 --- a/openspec/changes/polish-platform-interaction-design/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-08 diff --git a/openspec/changes/polish-platform-interaction-design/design.md b/openspec/changes/polish-platform-interaction-design/design.md deleted file mode 100644 index e75c561..0000000 --- a/openspec/changes/polish-platform-interaction-design/design.md +++ /dev/null @@ -1,75 +0,0 @@ -## Context - -The platform_web console already exposes the required first-party product areas and now has automated browser acceptance proving API-backed behavior across 首页、服务器管理、插件市场、用户管理、AI 提供商管理, server detail, and plugin controls. The next gap is not a new product capability; it is interaction quality. The console needs clearer hierarchy, denser-but-readable operational surfaces, stronger feedback, and responsive confidence while preserving its game operations visual contract. - -This change should stay inside platform_web interaction and visual polish. It must not introduce new business workflows, billing/cloud host flows, provider marketplaces, direct run access, or plugin transport shortcuts. It should use existing API-backed data and existing route contracts. - -## Goals / Non-Goals - -**Goals:** -- Turn UI dissatisfaction into objective acceptance criteria that implementation can complete and verify. -- Polish navigation, first-party route scanability, state feedback, command affordances, and server detail workflows. -- Keep the default black mecha console and optional magical-girl theme visually distinct while sharing the same layout and interaction model. -- Reuse `theme/tokens.ts`, `theme/base.css`, and shared surface classes before adding new styles. -- Require browser walkthrough evidence at desktop and mobile widths. -- Keep automated browser acceptance passing after polish. - -**Non-Goals:** -- Do not change platform, run, plugin, AI provider, authorization, lifecycle, log, artifact, or config semantics. -- Do not add billing, cloud host sales, agent-provider/cloud-provider workflows, or unrelated SaaS marketplace features. -- Do not replace the visual direction with generic opaque SaaS cards, one-off dark dashboards, or unrelated gradients. -- Do not add page-local fixed decorative spans, sparkles, sigils, snowflakes, hearts, moons, or other one-off background DOM. -- Do not create a fixed left-list/right-detail master-detail layout for server or plugin details. -- Do not bundle third-party character art or recognizable external visual assets. - -## Decisions - -1. Use accepted interaction criteria instead of taste-only language. - - Each polished page should have observable requirements: clear primary action, visible loaded/empty/error state, scannable hierarchy, no overlapping UI, readable status signals, responsive behavior, and preserved API-backed markers. This makes the implementation measurable in tests and browser walkthroughs. - -2. Polish shared primitives first. - - The implementation should start with existing shared classes such as `metric-card`, `overview-card`, `console-panel`, `catalog-card`, `server-card`, `server-detail-header`, `resource-table-wrap`, `provider-table-wrap`, `drawer-panel`, `confirm-panel`, `plugin-group`, and `operation-item`. If a new reusable pattern is needed, it belongs in `theme/base.css` with token-driven styling, not inside page-local opaque card systems. - -3. Preserve one route model across both themes. - - Black mecha and magical-girl should differ through tokens, frame treatments, materials, motifs, and `MagicalParticleLayer`, not through separate page implementations. The same route structure should remain usable and readable in both themes. - -4. Favor operational clarity over decoration. - - Lifecycle actions, destructive confirmations, logs, diffs, config review, operation results, warnings, and AI recommendations must remain text-readable, traceable, and not color-only. Decorative theme effects must stay behind operational surfaces and respect reduced motion. - -5. Demote low-value counters near management headers. - - Tiny counts such as installed plugin count, user count, role count, pending review count, provider count, enabled count, and model count are useful as context but should not become primary framed KPI cards. Management pages should keep scarce vertical space for the actual work surface: search/filter bars, plugin catalog, user table, and provider table. - -6. Use browser walkthroughs for acceptance, not screenshots alone. - - Completion requires exercising the actual routes and workflows in a browser at representative desktop and mobile widths. Screenshots can help debugging, but accepted evidence should focus on route behavior, visible controls, responsive layout, and absence of overlap or sensitive/fallback content. - -7. Prefer human-operable flows over raw DTO forms. - - Management pages should expose the same platform-backed actions, but the UI should bundle them into understandable operator flows: one status-change path per user, readable AI provider row actions, provider setup presets and secret-reference help, and maintenance triage entry points that connect failed jobs or stale endpoints to the next useful page. These additions remain presentation/workflow polish and do not add new authorization, run, plugin, or provider semantics. - -## Risks / Trade-offs - -- Visual polish can drift into scope expansion -> Keep tasks limited to platform_web interaction and theme presentation; do not change platform/run/plugin semantics. -- Theme work can create duplicate styles -> Require shared token/classes first and update `theme/README.md` only when new shared patterns are introduced. -- Responsive polishing can break automated acceptance markers -> Require automated browser acceptance after changes. -- Dense game-console visuals can harm readability -> Require operational clarity for logs, tables, diffs, warnings, and command results. -- Browser checks can be flaky -> Use stable routes, loaded states, viewport checks, and the existing local debug acceptance command. - -## Migration Plan - -1. Audit current first-party routes and server detail surfaces against the accepted interaction criteria. -2. Polish shared theme/layout primitives and route components in small focused batches. -3. Verify desktop and mobile browser walkthroughs across required routes and server detail workflows. -4. Run platform_web tests/build and automated browser acceptance. -5. Update task evidence and delivery stream pointers after verification passes. - -Rollback is straightforward because the change should stay in platform_web presentation and interaction code. Revert affected styles/components if a polish pass harms usability or breaks acceptance. - -## Open Questions - -- None currently. If implementation discovers a needed new product behavior, split it into a future OpenSpec instead of expanding this polish change. diff --git a/openspec/changes/polish-platform-interaction-design/proposal.md b/openspec/changes/polish-platform-interaction-design/proposal.md deleted file mode 100644 index 129c13c..0000000 --- a/openspec/changes/polish-platform-interaction-design/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -The console now has real API-backed coverage and automated browser acceptance, but the current interaction polish still depends on scattered page-level choices instead of explicit acceptance criteria. This change converts UI dissatisfaction into a concrete platform_web polish contract so the next implementation can improve usability without drifting into a generic SaaS dashboard or changing product scope. - -## What Changes - -- Define accepted interaction/design criteria for the required first-party areas: 首页、服务器管理、插件市场、用户管理、AI 提供商管理. -- Compress low-value management page summary counts into compact status context so primary list/table surfaces keep the scarce first-screen space. -- Define server detail workflow polish for lifecycle controls, logs, config, plugin controls, AI assistant, and operation history. -- Require responsive desktop/mobile walkthrough coverage and no visible overlap, clipped text, unreadable panels, or inaccessible control states. -- Require the polish to preserve the existing black mecha default theme, magical-girl alternate theme, translucent game-operations surfaces, grouped navigation, and shared theme primitives. -- Require compatibility with the automated browser acceptance suite so visual/interaction polish does not weaken API-backed route proof or safety scanning. -- No breaking product behavior, API, authorization, plugin, run, billing, cloud-host, or provider-marketplace changes are expected. - -## Capabilities - -### New Capabilities - -- `platform-interaction-design-polish`: Defines accepted interaction and visual polish requirements for platform_web first-party console areas, server detail workflows, responsive behavior, theme preservation, browser walkthrough evidence, and automated acceptance compatibility. - -### Modified Capabilities - -- None. - -## Impact - -- Affected roots: `platform_web/`, platform_web documentation, and OpenSpec delivery stream files. -- Expected implementation areas: shared theme surfaces/classes, route/page layouts, command affordances, responsive behavior, state feedback, browser walkthrough evidence, and test coverage. -- Validation impact: requires `cd platform_web && npm run typecheck && npm test && npm run build`, automated browser acceptance against the local debug stack, `scripts/check-structure.sh`, and `openspec validate polish-platform-interaction-design --strict`. diff --git a/openspec/changes/polish-platform-interaction-design/specs/platform-interaction-design-polish/spec.md b/openspec/changes/polish-platform-interaction-design/specs/platform-interaction-design-polish/spec.md deleted file mode 100644 index 588a70e..0000000 --- a/openspec/changes/polish-platform-interaction-design/specs/platform-interaction-design-polish/spec.md +++ /dev/null @@ -1,84 +0,0 @@ -## ADDED Requirements - -### Requirement: First-party routes provide accepted interaction polish -The platform_web console SHALL provide polished, scannable, API-backed interaction surfaces for the required first-party areas without changing product scope. - -#### Scenario: Home route has clear operational hierarchy -- **WHEN** an operator opens 首页 -- **THEN** the route MUST present loaded platform overview state, key resource/health signals, game/plugin counts or equivalent operational summaries, and a clear refresh or recovery affordance without visible overlap or clipped primary labels - -#### Scenario: Server management route is action-oriented and scannable -- **WHEN** an operator opens 服务器管理 -- **THEN** the route MUST make server identity, lifecycle state, run assignment, filtering, creation entry point, and drill-in affordance easy to scan without using a fixed left-list/right-detail master-detail layout - -#### Scenario: Plugin marketplace route communicates trust and capability -- **WHEN** an operator opens 插件市场 -- **THEN** the route MUST present plugin identity, installed state, manifest reference, lifecycle capabilities, platform-mediated permissions, bridge actions, and validation state in a readable structure without unsafe runtime transport details - -#### Scenario: User management route supports account operations clearly -- **WHEN** an operator opens 用户管理 -- **THEN** the route MUST present API-connected account data, roles/statuses, create/edit affordances, and empty/error/loading states with text labels and non-color-only status cues - -#### Scenario: AI provider route keeps sensitive settings understandable -- **WHEN** an operator opens AI 提供商管理 -- **THEN** the route MUST present provider identity, connection status, relay mode, model/default-model information, and redacted key references without exposing raw keys or making status dependent on color alone - -#### Scenario: Management page counters stay secondary -- **WHEN** an operator opens 插件市场、用户管理, or AI 提供商管理 -- **THEN** small summary counts such as installed plugins, bridge actions, validation failures, users, roles, pending reviews, providers, enabled providers, and models MUST render as compact contextual status instead of large framed KPI cards that displace the primary list, grid, table, search, or filter work surface - -### Requirement: Server detail workflows are polished without direct run access -The platform_web server detail route SHALL provide polished workflow surfaces for lifecycle, logs, config, plugin controls, AI assistant, and operation history while preserving platform-mediated boundaries. - -#### Scenario: Server detail header and tabs show stable context -- **WHEN** an operator opens `#/servers/server-local-debug` or another server detail route -- **THEN** the route MUST keep server name, server ID, plugin version, run node, lifecycle state, and tab navigation visible and readable across desktop and mobile widths - -#### Scenario: Lifecycle commands have safe feedback -- **WHEN** an operator views or triggers lifecycle controls -- **THEN** start/stop or equivalent controls MUST have clear labels, enabled/disabled states, confirmation or progress feedback where appropriate, and operation-history visibility without browser or plugin pages contacting run directly - -#### Scenario: Logs, config, artifacts, and operation history remain traceable -- **WHEN** an operator uses detail tabs for logs, config, plugin controls, AI assistant, or operation history -- **THEN** each surface MUST show meaningful loaded/empty/error states, logical IDs or safe references, readable timestamps/statuses, and no raw host paths, sockets, credentials, or plugin-owned transport details - -### Requirement: Visual system contract is preserved during polish -The platform_web polish SHALL preserve the existing game operations visual direction and shared theme architecture. - -#### Scenario: Shared theme primitives drive surfaces -- **WHEN** implementation changes route or component presentation -- **THEN** it MUST reuse or extend shared tokens/classes in `theme/tokens.ts` and `theme/base.css` rather than introducing page-local opaque card systems, one-off dark dashboards, or unrelated visual languages - -#### Scenario: Black mecha and magical-girl themes stay distinct -- **WHEN** an operator uses the default black mecha theme or optional magical-girl theme -- **THEN** both themes MUST keep their theme-specific materials, readable translucent surfaces, grouped large-entry navigation, and background visibility while sharing the same product workflows - -#### Scenario: Global decorative effects remain centralized -- **WHEN** implementation changes ambient or decorative effects -- **THEN** full-workspace theme-aware effects MUST remain in `components/MagicalParticleLayer.tsx` and MUST NOT use page-local fixed decorative DOM/CSS elements - -### Requirement: Responsive browser walkthrough proves polish acceptance -The polish change SHALL require browser verification across required routes and representative viewport sizes before completion. - -#### Scenario: Desktop and mobile walkthroughs cover required routes -- **WHEN** implementation claims the polished UI is accepted -- **THEN** browser walkthrough evidence MUST cover 首页、服务器管理、插件市场、用户管理、AI 提供商管理, server detail workflows, and representative desktop and mobile viewport widths - -#### Scenario: Walkthrough rejects layout regressions -- **WHEN** browser walkthroughs inspect accepted routes -- **THEN** they MUST reject visible overlap, clipped primary text, unreachable primary controls, unreadable loaded/error/empty states, and navigation states that hide required first-party areas - -#### Scenario: Automated acceptance remains compatible -- **WHEN** polish implementation is complete -- **THEN** `scripts/browser-acceptance.sh` with the documented local debug environment MUST still pass and MUST continue proving API-backed content, fallback rejection, forbidden-fragment scanning, and platform-mediated plugin/server operation proof - -### Requirement: Polish verification commands are concrete -The OpenSpec tasks SHALL list reproducible commands that prove implementation quality. - -#### Scenario: Verification commands are available -- **WHEN** contributors read implementation tasks -- **THEN** they MUST find concrete commands for platform_web typecheck/tests/build, automated browser acceptance, structure checks, and strict OpenSpec validation - -#### Scenario: Evidence is recorded before task completion -- **WHEN** implementation tasks are marked complete -- **THEN** task evidence MUST record the browser walkthrough coverage, automated acceptance command output or evidence path, platform_web verification, `scripts/check-structure.sh`, and `openspec validate polish-platform-interaction-design --strict` diff --git a/openspec/changes/polish-platform-interaction-design/tasks.md b/openspec/changes/polish-platform-interaction-design/tasks.md deleted file mode 100644 index 0397b5c..0000000 --- a/openspec/changes/polish-platform-interaction-design/tasks.md +++ /dev/null @@ -1,79 +0,0 @@ -## 1. Interaction Audit and Acceptance Criteria - -- [x] 1.1 Audit 首页、服务器管理、插件市场、用户管理、AI 提供商管理, and server detail against the accepted interaction polish requirements. -- [x] 1.2 Identify any visible overlap, clipped labels, weak hierarchy, unclear primary actions, missing empty/error/loading states, or color-only status cues. -- [x] 1.3 Confirm the implementation scope remains platform_web polish only and does not add billing, cloud host sales, provider marketplace workflows, direct run access, or plugin transport shortcuts. -- [x] 1.4 Map every planned UI change to shared route components, shared theme primitives, or documented shared CSS additions. - -## 2. Shared Theme and Layout Polish - -- [x] 2.1 Reuse or extend `theme/tokens.ts` and `theme/base.css` for any new shared materials, frames, command states, tables, drawers, dialogs, or responsive primitives. -- [x] 2.2 Preserve black mecha as the default theme and magical-girl as the alternate theme, with distinct token-driven materials and readable translucent surfaces. -- [x] 2.3 Keep grouped large-entry navigation, large icon badges, bold Chinese labels, expandable child rows, active frames, and single-column/double-column density behavior. -- [x] 2.4 Keep full-workspace decorative effects centralized in `components/MagicalParticleLayer.tsx`; do not add page-local fixed decorative DOM/CSS motifs. -- [x] 2.5 Keep framed repeated items at 8px-or-less radii unless a native pill/circle control shape is required. - -## 3. First-Party Route Polish - -- [x] 3.1 Polish 首页 hierarchy so loaded platform overview, resource/health signals, game/plugin counts, and refresh/recovery affordance are clear at desktop and mobile widths. -- [x] 3.2 Polish 服务器管理 so server identity, lifecycle state, run assignment, filters, creation entry point, and drill-in affordance are scannable without a fixed left-list/right-detail layout. -- [x] 3.3 Polish 插件市场 so plugin identity, installed state, manifest reference, lifecycle capabilities, permissions, bridge actions, and validation state are readable and safe. -- [x] 3.4 Polish 用户管理 so API-connected account rows, role/status cues, create/edit affordances, and loading/empty/error states are clear and not color-only. -- [x] 3.5 Polish AI 提供商管理 so provider status, relay mode, model/default-model details, and redacted key references are readable without exposing raw keys. - -## 4. Server Detail Workflow Polish - -- [x] 4.1 Polish server detail header and tabs so server name, ID, plugin version, run node, lifecycle state, and tab navigation remain visible across desktop and mobile widths. -- [x] 4.2 Polish lifecycle command states so start/stop controls have clear labels, disabled/progress behavior, confirmations or feedback where appropriate, and operation-history visibility. -- [x] 4.3 Polish logs, config, plugin controls, AI assistant, artifacts, and operation history surfaces with meaningful loaded/empty/error states, safe logical IDs, readable timestamps, and traceable outcomes. -- [x] 4.4 Confirm server detail and plugin controls remain platform-mediated and do not expose raw host paths, sockets, credentials, direct run URLs, or plugin-owned transports. - -## 5. Browser Walkthrough and Automated Acceptance - -- [x] 5.1 Run a browser walkthrough at a desktop viewport across 首页、服务器管理、插件市场、用户管理、AI 提供商管理, server detail, lifecycle controls, plugin controls, logs/config/AI/operation-history tabs, and record evidence. -- [x] 5.2 Run a browser walkthrough at a mobile viewport across the same required first-party areas and server detail workflow surfaces, and record evidence. -- [x] 5.3 Verify both black mecha and magical-girl themes preserve readable surfaces, distinct theme treatments, navigation clarity, and background visibility. -- [x] 5.4 Run `LOCAL_DEBUG_PLATFORM_PORT=18189 LOCAL_DEBUG_WEB_PORT=5183 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-acceptance scripts/browser-acceptance.sh` and record the evidence path. - -## 6. Verification and Stream Update - -- [x] 6.1 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 6.2 Run `scripts/check-structure.sh` and record evidence. -- [x] 6.3 Run `openspec validate polish-platform-interaction-design --strict` and record evidence. -- [x] 6.4 If structural theme rules or shared style contracts change, update `platform_web/theme/README.md` and any relevant tests in the same change. -- [x] 6.5 Update `openspec/changes/architecture-delivery-stream/delivery-plan.md` and `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` after implementation evidence exists. - -## 7. Management Header Density Correction - -- [x] 7.1 Replace large framed management summary KPI cards in 插件市场、用户管理, and AI 提供商管理 with compact contextual status chips. -- [x] 7.2 Add regression tests proving those management headers render `page-summary-chip` instead of `metric-card`. -- [x] 7.3 Run focused frontend tests, `scripts/check-structure.sh`, and `openspec validate polish-platform-interaction-design --strict`; record evidence. - -## 8. Human Workflow Polish Follow-up - -- [x] 8.1 Replace duplicate 用户管理 status/deactivation buttons with one status selector flow and explicit confirmation when disabling access. -- [x] 8.2 Add user invitation, review, server-scope, and role-impact guidance without adding new backend authorization semantics. -- [x] 8.3 Replace AI provider icon-only row actions with readable action labels and a compact "more" menu for lower-frequency enable/retire actions. -- [x] 8.4 Add AI provider setup guidance: presets, secret-reference help, save-before validation, saved-configuration testing, and model discovery fill-in. -- [x] 8.5 Add 系统维护 triage entry points for endpoint heartbeat/capacity, failed jobs, and related server/log navigation links. -- [x] 8.6 Run focused frontend tests and typecheck; use the existing 5173 browser instance to spot-check the changed pages. - -## Evidence - -- `platform_web/pages/UsersPage.tsx`: added explicit loading, API fallback error, empty-state, and accessible row action labels for 用户管理. -- `platform_web/pages/AiProvidersPage.tsx`: added an explicit empty-state for filtered AI provider lists while preserving redacted `apiKeyRef` display. -- `platform_web/pages/ServersPage.tsx`: polished create workflow feedback with an inline result strip. -- `platform_web/pages/PluginsPage.tsx`: polished plugin detail framing and action strip behavior. -- `platform_web/theme/base.css`: hardened shared action strips, server toolbars, catalog cards, plugin detail panels, result strips, responsive grids, and table/workspace min-width behavior. -- `platform_web/acceptance/browser-acceptance.mjs`: expanded browser acceptance to record desktop/mobile walkthroughs for black mecha and magical-girl themes, route marker checks, visible-layout checks, API-backed route proof, plugin controls, and operation-history proof. -- Header density correction evidence: `platform_web/components/PageFrame.tsx` and `platform_web/pages/AiProvidersPage.tsx` now render management summary counts as `page-summary-chip`; `platform_web/pages/PluginsPage.test.tsx`, `platform_web/pages/UsersPage.test.tsx`, and `platform_web/pages/AiProvidersPage.test.tsx` assert those headers no longer render `metric-card`. -- Focused frontend evidence: `cd platform_web && npm test -- PluginsPage.test.tsx UsersPage.test.tsx AiProvidersPage.test.tsx`, `cd platform_web && npm run typecheck`, and `cd platform_web && npm run build` passed for the header density correction. -- Browser evidence: `LOCAL_DEBUG_PLATFORM_PORT=18189 LOCAL_DEBUG_WEB_PORT=5183 LOCAL_DEBUG_ROOT=/private/tmp/browser-local-debug-acceptance scripts/browser-acceptance.sh` passed; evidence file `/private/tmp/browser-local-debug-acceptance/browser-acceptance/browser-acceptance-evidence.json` records 7 required routes plus 4 walkthrough scenarios: desktop/mobile black mecha and desktop/mobile magical-girl. -- Frontend evidence: `cd platform_web && npm run typecheck`, `cd platform_web && npm test` (11 files / 49 tests), and `cd platform_web && npm run build` passed. -- Structure evidence: `scripts/check-structure.sh` passed. -- OpenSpec evidence: `openspec validate polish-platform-interaction-design --strict` passed. -- Human workflow polish evidence: `platform_web/pages/UsersPage.tsx` now uses one status selector/apply path, keeps disable confirmation, renames create flow to 邀请用户, and shows invite/review/server-scope/role-impact guidance. -- AI provider workflow evidence: `platform_web/pages/AiProvidersPage.tsx` now shows row actions as 测试 / 模型 / 编辑 / 更多, moves enable/retire into the more menu, adds provider presets, secret-reference help, 保存前检查, saved-configuration testing, and saved-model discovery fill-in. -- Maintenance triage evidence: `platform_web/pages/MaintenancePage.tsx` now loads endpoints, jobs, servers, and audit events to show node heartbeat/capacity detail, recent failed jobs, retry dispatch, related server links, and log-chain entry copy. -- Human workflow verification: `cd platform_web && npm test -- UsersPage.test.tsx AiProvidersPage.test.tsx ConsolePages.test.tsx`, `cd platform_web && npm run typecheck`, and `cd platform_web && npm run build` passed. `scripts/check-structure.sh` passed. `openspec validate polish-platform-interaction-design --strict` passed; only PostHog telemetry flushing failed due restricted network after validation succeeded. -- Browser spot-check evidence: existing `http://127.0.0.1:5173` was opened and logged in with the local test operator. `#/aiProviders` showed readable row actions and edit dialog guidance with no clipped target buttons and no `api.example.test`; `#/users` showed 邀请用户, 审核申请, 绑定服务器范围, 角色影响, 应用状态, no duplicate “停用用户” label, and no clipped target buttons; `#/maintenance` showed 系统维护, 节点详情, 最近失败任务, heartbeat/status context, and the follow-up commit adds visible 维护排障入口 / 查看相关服务器 / 查看日志链路 copy for empty-data states. diff --git a/openspec/changes/polish-theme-frame-accessories/design.md b/openspec/changes/polish-theme-frame-accessories/design.md deleted file mode 100644 index aa0208d..0000000 --- a/openspec/changes/polish-theme-frame-accessories/design.md +++ /dev/null @@ -1,35 +0,0 @@ -# Design - -## Overview - -Theme accessories are implemented as generated SVG data URLs stored in palette variables. Shared CSS primitives consume the variables so cards, panels, tables, drawers, dialogs, plugin groups, operation history, and active sidebar entries gain theme-specific corner ornaments without page-level decoration. - -## Theme Assets - -The new variables are: - -- `--frame-accessory-top` -- `--frame-accessory-bottom` -- `--frame-accessory-opacity` -- `--frame-accessory-size` - -`mecha-black` uses compact SVG motifs for spacecraft, robot/vehicle silhouettes, radar rings, and planet-like targeting circles. - -`magical-girl` uses compact SVG motifs for winged hearts, stars, wands, ribbons, and magic-circle rings. - -## CSS Application - -Shared `::after` layers compose: - -- Top accessory. -- Bottom accessory. -- Existing shine/jelly material. - -Table wrappers, plugin groups, and operation items get matching `::after` layers so dense operational surfaces still inherit the theme identity. The layers remain pointer-events disabled and non-interactive. - -## Constraints - -- Keep all ornaments in shared theme CSS/tokens. -- Preserve 8px-or-less frame radii for repeated surfaces. -- Avoid external iconfont dependency unless generated assets become insufficient. -- Keep operational text readable by using transparent SVG backgrounds and controlled opacity. diff --git a/openspec/changes/polish-theme-frame-accessories/proposal.md b/openspec/changes/polish-theme-frame-accessories/proposal.md deleted file mode 100644 index c273b4b..0000000 --- a/openspec/changes/polish-theme-frame-accessories/proposal.md +++ /dev/null @@ -1,34 +0,0 @@ -# Polish theme frame accessories - -## Summary - -Add theme-specific ornamental accessories to shared platform web frames so each first-party theme has distinct border personality beyond color swaps. - -## Motivation - -The current black mecha and magical-girl themes already differ in palette, material, and global effects, but repeated cards and panels still share a similar decorative language. The requested direction calls for each theme border to carry its own accessories: - -- Mecha frames should read as robot, ship, planet, radar, and cockpit hardware. -- Magical-girl frames should read as hearts, stars, wands, ribbons, and magic-circle motifs. - -These ornaments should remain in the shared theme system rather than page-local decorations. - -## Scope - -- Add generated SVG accessory assets as CSS theme variables in `platform_web/theme/tokens.ts`. -- Apply accessory variables to shared framed surfaces in `platform_web/theme/base.css`. -- Include active sidebar item ornamentation so navigation states also inherit the theme identity. -- Update token tests to lock the accessory variables. - -## Out of Scope - -- New third-party icon dependencies or bundled recognizable character art. -- Page-local fixed decorative spans. -- New theme families, route changes, or backend behavior. - -## Verification - -- Run platform web tests and build. -- Run `scripts/check-structure.sh`. -- Run `openspec validate polish-theme-frame-accessories --strict`. -- Perform a browser walkthrough of the platform web shell in both themes. diff --git a/openspec/changes/polish-theme-frame-accessories/specs/platform-web-theme-accessories/spec.md b/openspec/changes/polish-theme-frame-accessories/specs/platform-web-theme-accessories/spec.md deleted file mode 100644 index c441ccf..0000000 --- a/openspec/changes/polish-theme-frame-accessories/specs/platform-web-theme-accessories/spec.md +++ /dev/null @@ -1,72 +0,0 @@ -# platform-web-theme-accessories Specification - -## ADDED Requirements - -### Requirement: Theme frame accessories - -Shared platform web framed surfaces SHALL render theme-specific ornamental accessories. - -#### Scenario: Black mecha theme is active - -- **WHEN** the active palette is `mecha-black` -- **THEN** shared framed surfaces include mecha accessory motifs such as spacecraft, robot hardware, radar, planet, or cockpit details -- **AND** the motifs are provided through shared theme variables rather than page-local decorative DOM - -#### Scenario: Magical-girl theme is active - -- **WHEN** the active palette is `magical-girl` -- **THEN** shared framed surfaces include magical accessory motifs such as hearts, stars, magic wands, ribbons, or magic circles -- **AND** the motifs are provided through shared theme variables rather than page-local decorative DOM -- **AND** repeated sibling items rotate through distinct motifs instead of repeating one identical accessory -- **AND** each framed item uses one compact edge badge rather than multiple oversized illustrations - -### Requirement: Navigation active frame accessories - -Active sidebar navigation entries SHALL inherit the active theme's frame accessory language. - -#### Scenario: User changes theme while a route is active - -- **WHEN** a sidebar route is active -- **AND** the user switches between first-party themes -- **THEN** the active sidebar frame changes accessory motifs to match the selected theme -- **AND** route order and labels remain unchanged - -### Requirement: Live theme isolation - -Theme switching SHALL replace the complete active palette without retaining visual variables or shell labels from the previous palette. - -#### Scenario: User switches from magical-girl to black mecha - -- **WHEN** the active palette is `magical-girl` -- **AND** the user selects `mecha-black` -- **THEN** the root theme marker, shared surface materials, sidebar subtitle, and palette strip update to black mecha immediately -- **AND** magical-girl accessory variables from the previous palette do not remain active on mecha surfaces - -### Requirement: Single frame ownership - -An operational content region SHALL render at most one ornamental frame at each visual hierarchy level. - -#### Scenario: State view is nested inside a shared framed surface - -- **WHEN** an empty, loading, or error state is rendered inside a `console-panel`, card, table wrapper, plugin group, or operation item -- **THEN** the parent surface retains the active theme frame and accessory -- **AND** the nested state view renders as unframed content without a second border, panel fill, or accessory pseudo-element - -### Requirement: Uploaded background frame visibility - -Uploaded backgrounds SHALL remain visually legible while preserving the selected theme's frame identity. - -#### Scenario: Magical-girl theme uses an uploaded background - -- **WHEN** the active palette is `magical-girl` -- **AND** the user has configured an uploaded background -- **THEN** shared foreground surfaces use translucent neutral-pink crystal glass rather than an opaque maroon color mask -- **AND** the uploaded image remains recognizable behind the foreground surfaces -- **AND** compact heart, star, wand, moon, ribbon, crystal, and magic-circle accessories visibly cross the panel border instead of being hidden inside the panel -- **AND** accessory pseudo-elements do not add a color wash over the configured background - -#### Scenario: Magical-girl theme uses a built-in background - -- **WHEN** the active palette is `magical-girl` -- **AND** no uploaded background is configured -- **THEN** shared framed surfaces retain compact, immediately visible magical accessories that extend beyond their borders without crowding content diff --git a/openspec/changes/polish-theme-frame-accessories/tasks.md b/openspec/changes/polish-theme-frame-accessories/tasks.md deleted file mode 100644 index c6617fa..0000000 --- a/openspec/changes/polish-theme-frame-accessories/tasks.md +++ /dev/null @@ -1,37 +0,0 @@ -# Tasks - -- [x] Add generated mecha and magical-girl SVG accessory variables to theme tokens. -- [x] Apply accessory variables to shared framed surfaces and active sidebar states. -- [x] Extend theme token tests for accessory variables. -- [x] Run tests, structure check, OpenSpec validation, and browser walkthrough. -- [x] Recalibrate uploaded-background glass so configured artwork remains recognizable. -- [x] Enlarge and strengthen magical-girl frame accessories across shared surfaces. -- [x] Repeat browser walkthrough and all repository validation after the correction. -- [x] Reduce magical-girl accessories to compact single-motif edge badges. -- [x] Rotate distinct heart, wand, moon, ribbon, crystal, and circle assets across repeated items. -- [x] Verify uploaded backgrounds remain unobscured after the decoration-density correction. -- [x] Clear stale palette variables and synchronize shell chrome after live theme switches. -- [x] Suppress nested state-view frames when a shared parent already owns the decoration. -- [x] Re-run browser theme switching and repository validation for the isolation fix. - -## Evidence - -- `npm test`: 11 files passed, 49 tests passed. -- `npm run typecheck`: passed. -- `npm run build`: passed. -- `scripts/check-structure.sh`: passed. -- `openspec validate polish-theme-frame-accessories --strict`: passed. -- Browser walkthrough: entered local fallback workbench, verified mecha active navigation uses generated spacecraft/radar accessories, then switched through Profile Settings to magical-girl / 粉月魔法阵 and verified shared panels render heart/star/wand/magic-circle accessories. -- Follow-up browser walkthrough after visual feedback: reduced magical-girl accessory size to `70px 42px, 78px 54px`, lowered opacity to `0.44`, moved frame accessories under content (`::after z-index: 0`, content z-index above), and confirmed the custom-background overlay token is `transparent`. -- Second follow-up after screenshot feedback: converted the SVG accessories to small line-art edge accents, locked token accessory sizes to max `48px`, reduced custom-background surface blur to `4px`, removed theme color tint from uploaded-background panel material, lowered custom-background ambient particles to `0.08`, and verified a headless Chrome screenshot at `.tmp/theme-visual-check/profile-custom-background.png` with `data-custom-background="true"`, `--custom-background-overlay: transparent`, and profile panels using `blur(4px) saturate(1.02)`. -- Final color audit after palette feedback: rebalanced uploaded-background mode so the page uses `--custom-background-overlay: transparent`, keeps controls on readable dark glass, and removes large-panel blur in favor of low-opacity ink glass; verified fresh headless screenshots at `.tmp/theme-color-audit/mecha-uploaded.png` and `.tmp/theme-color-audit/magical-uploaded.png` where uploaded artwork remains visible behind the profile/settings panels. -- Custom-background readability pass after latest screenshots: removed the visible theme-color mask from uploaded backgrounds while dimming the wallpaper layer itself, increased content-panel opacity/blur only on foreground surfaces, added dark glass treatment for server toolbars and state/error panels, and added a compact title backplate so bright uploads do not wash out page headers; verified screenshots at `.tmp/custom-background-audit/servers.png` and `.tmp/custom-background-audit/profile-settings.png` with `data-custom-background="true"`, `filter: saturate(0.72) contrast(0.88) brightness(0.82)`, and panel backdrop `blur(16px) saturate(0.78)`. -- Magical-girl correction after latest feedback: restored visible pink moonlight panel material and frame accessories under custom uploaded backgrounds, prevented the generic custom-background opacity rule from suppressing magical-girl decorations, and moved magical-girl frame accessories outside the panel border with larger edge-overhanging assets; verified screenshots at `.tmp/magical-custom-background-check.png` and `.tmp/magical-preset-border-check.png`. -- Final uploaded-background correction: replaced opaque maroon surfaces with low-opacity neutral-pink crystal glass, restored the uploaded artwork to `saturate(0.92) contrast(0.96) brightness(0.94)`, removed backdrop blur from large magical-girl custom-background panels, added real `::after` accessory layers for server toolbars and state views, and strengthened generated heart/star/wand/magic-circle SVGs so they cross the border at `132-154px` with `0.80-0.84` opacity. Browser walkthrough at `1440x1000` confirmed the configured anime artwork remains recognizable, accessories are immediately visible outside the frame, and the browser console has no errors; evidence saved at `.tmp/magical-custom-background-final.png`. -- Final verification: `npm --prefix platform_web test` passed 11 files and 49 tests; `npm --prefix platform_web run typecheck`, `npm --prefix platform_web run build`, and `scripts/check-structure.sh` passed; `openspec validate polish-theme-frame-accessories --strict` reported the change valid with exit code `0` (telemetry flush warnings only). -- Decoration-density correction after item-level feedback: replaced the two oversized magical illustrations on each surface with one `68px × 44px` edge-overhanging badge, generated six distinct inline SVG motifs (heart wings, star wand, crescent moon, ribbon, crystal, and magic circle), and rotate them through repeated cards with shared CSS variables and `:nth-child()` rules. In-app browser verification on `#/profile` with `data-theme-palette="magical-girl"` and `data-custom-background="true"` confirmed the three metric items resolve to `heart`, `wand`, and `moon`, lower settings panels resolve to different motifs, the configured background remains fully recognizable, and browser console errors are empty. -- Decoration-density verification: `npm --prefix platform_web test` passed 11 files and 49 tests; `npm --prefix platform_web run typecheck`, `npm --prefix platform_web run build`, `scripts/check-structure.sh`, and `openspec validate polish-theme-frame-accessories --strict` passed (OpenSpec telemetry flush warnings only). -- Theme-isolation correction: `applyThemePalette` now removes the union of prior palette variables before applying the selected palette and publishes a live palette-change event so AppShell updates its subtitle and swatch strip without a remount. -- Single-frame correction: nested `.state-view` elements inside shared framed surfaces now resolve to transparent, borderless, shadowless content with both pseudo-elements disabled, while the parent surface keeps the active theme accessory. -- Browser walkthrough with an uploaded custom background: switched `magical-girl` to `mecha-black` and confirmed `data-theme-palette="mecha-black"`, sidebar subtitle `黑色机甲 / OPS`, cyan `--accent: #48e6ff`, and no remaining inline `--frame-accessory-heart`; switched back to magical-girl and confirmed only parent panel accessories render; restored mecha as the final state. Two nested home-page state views resolved to `border: 0px`, transparent backgrounds, and `::after content: none`; browser warnings/errors were empty. -- Isolation verification: `npm --prefix platform_web test` passed 12 files and 51 tests; `npm --prefix platform_web run typecheck`, `npm --prefix platform_web run build`, and `scripts/check-structure.sh` passed; strict OpenSpec validation reported the change valid (telemetry flush warnings only). diff --git a/openspec/changes/rebuild-scum-plugin-owned-data/.openspec.yaml b/openspec/changes/rebuild-scum-plugin-owned-data/.openspec.yaml deleted file mode 100644 index 4af8641..0000000 --- a/openspec/changes/rebuild-scum-plugin-owned-data/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-14 diff --git a/openspec/changes/rebuild-scum-plugin-owned-data/design.md b/openspec/changes/rebuild-scum-plugin-owned-data/design.md deleted file mode 100644 index 7517f2d..0000000 --- a/openspec/changes/rebuild-scum-plugin-owned-data/design.md +++ /dev/null @@ -1,42 +0,0 @@ -# Design - -## Ownership - -Platform owns authentication, server-instance authorization, durable storage mechanics, collection scoping, and transaction boundaries. It does not interpret collection payloads. - -The SCUM plugin owns collection names such as `scum_users`, schemas, upsert keys, data transformations, gifts, map geometry, and UI behavior. It declares database/file/log work in the manifest; Platform dispatches those declarations to Run, and Run executes on the machine. - -## Generic Platform Data Contract - -The generic record is scoped by `pluginId`, `serverInstanceId`, `collection`, and `key`, with an opaque JSON `value` and timestamps. The platform validates scope and authorization only. A page bridge exposes list/put/delete and atomic put/delete transaction methods to plugin bundles. - -SCUM uses stable `scum_*` collection names in the Platform database. Game database versions do not become Platform branches: a new SCUM schema updates the plugin SQL, result schema, mappings, and parser declarations while preserving the normalized collection contract. - -## SCUM Data Flow - -1. The plugin declares SQLite `sqlRef` assets, automatic cadence, collection row targets, config maps, and log projections in its own data pack and manifest. `PRAGMA user_version` is diagnostic evidence, not a Platform version switch. -2. When Run polls for work, Platform creates every due declared query before selecting the next job. This naturally follows the existing two-second polling channel and works without a UI or a new background daemon. -3. Run executes the declared SQL asset and returns structured rows. Platform applies only the declared collection, key, mapping, merge/replace mode, and fixed values. -4. Full snapshot targets delete records absent from a successful complete result. Merge targets preserve stdout presence fields while SQLite later adds profile, economy, squad, and coordinate facts. -5. Durable stdout batches are evaluated against plugin-declared ordered regex steps. Named captures with the same name must agree across the sequence; the complete sequence emits one stable event value and resets its bounded per-stream state. -6. The SCUM BattlEye declaration correlates `reported as player N` with `Player N SteamID (assumed)` by slot. Steam ID is the `scum_users` key; names are mutable display fields and are never identity keys. -7. The presence policy reads the existing user before mutation. A missing record receives the new-player announcement. A record whose last login is inside ten minutes is updated at most once and receives no duplicate announcement. An older record receives the returning-player announcement. -8. Announcement text and `#announce` command syntax belong to the SCUM plugin declaration. Platform only renders captured placeholders and queues the already-declared command through the existing Run channel. -9. The SCUM page polls Platform collections for display freshness. It never dispatches SQLite queries and exposes no manual synchronization button. -10. Gift delivery and activity commands use the existing generic Game Client Bridge queue exposed by the plugin-page host. - -## Declared Cadences - -- `scum.player.profile`, `scum.vehicles`, and `scum.positions`: 3 seconds. -- `scum.squads` and `scum.squad-members`: 1800 seconds. -- Flags, native event/task observations, and native timed-gift observations use plugin-owned slower cadences appropriate to those datasets. - -Cadence is measured from the latest matching job attempt. A still-active matching query suppresses another job, and idempotency keys include the server, template, and cadence bucket. - -## Real Data Evidence - -The provided complete database is `/Users/tasia/Downloads/SCUM/SCUM.db`; the similarly named file under `Logs/` is empty. The complete database reports SQLite `user_version=57`, contains 162 tables, and passes `quick_check`. All nine packaged SQL files execute against it. The existing users query starts at `user_profile` and therefore misses one real account; it must start at `user` and left join optional profile/prisoner data so stdout-created Steam identities merge correctly. - -## Compatibility - -Recent SCUM-specific Platform routes, types, repository tables, and game-gift APIs are removed. The retained generic bridge remains usable by other game plugins without SCUM imports or switches. diff --git a/openspec/changes/rebuild-scum-plugin-owned-data/evidence/scum-local-data-baseline-2026-08-18.md b/openspec/changes/rebuild-scum-plugin-owned-data/evidence/scum-local-data-baseline-2026-08-18.md deleted file mode 100644 index 5face9a..0000000 --- a/openspec/changes/rebuild-scum-plugin-owned-data/evidence/scum-local-data-baseline-2026-08-18.md +++ /dev/null @@ -1,32 +0,0 @@ -# SCUM Local Data Baseline - 2026-08-18 - -## Scope - -This is redacted structural evidence from the user-authorized local SCUM download and legacy projects. It records no player names, Steam IDs, IP addresses, coordinates, credentials, or row bodies. - -## Database - -- `/Users/tasia/Downloads/SCUM/Logs/SCUM.db` is an empty zero-byte file and is not usable evidence. -- `/Users/tasia/Downloads/SCUM/SCUM.db` is the complete database: SQLite `user_version=57`, 162 tables, `quick_check=ok`. -- Aggregate rows: 74 accounts, 73 profiles, 72 prisoners, 7 squads, 18 squad members, 313 vehicles, 5 bases, 5 flags, 0 native event rounds, and 2 native timed-gift completion records. -- All nine packaged v57 SQL assets execute against the complete database. -- The account/profile cardinality proves that user extraction must start from `user` and left join the optional profile chain. Steam ID is the stable collection identity; profile ID, prisoner ID, and display name are attributes. -- The current map query returns player, vehicle, base, and flag points and the observed coordinates fit the declared SCUM island bounds. - -## Logs - -- The download contains 568 UTF-16LE/LF log files across 19 filename prefixes. Filenames use a server-start timestamp and a file can continue growing for many hours; tailing therefore requires an offset cursor per file rather than a daily filename assumption. -- Each file starts with a blank line and one `Game version:` metadata line that must not become a business event. -- Login file logs contain single-line login/logout records with optional coordinates. -- The user-supplied BattlEye `reported`, `connected`, `SteamID`, and GUID sequence is supervised process stdout and does not occur in the downloaded file logs. It requires a separate ordered stdout projection. - -## Configuration - -- The configuration directory contains `ServerSettings.ini`, list-based access files, `EconomyOverride.json`, `RaidTimes.json`, `Notifications.json`, and engine input/user settings. -- `ServerSettings.ini` reports settings version 7 and contains hundreds of `scum.*` keys. Config reads and patches must preserve unknown keys rather than reconstructing the file from a short allowlist. - -## Legacy Behavior - -- The legacy robot continuously updated players and positions in the background and queued welcome text through the server command channel; browser presence was never the acquisition trigger. -- Legacy welcome behavior distinguishes first registration from a returning player and suppresses rapid repeated observations. This change uses the explicitly requested ten-minute window. -- The legacy implementation contains field-order mistakes in its user creation branch, so only its product behavior and stable field intent are reused, not its SQL/value assignments. diff --git a/openspec/changes/rebuild-scum-plugin-owned-data/proposal.md b/openspec/changes/rebuild-scum-plugin-owned-data/proposal.md deleted file mode 100644 index 44925f7..0000000 --- a/openspec/changes/rebuild-scum-plugin-owned-data/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -# Rebuild SCUM Plugin-Owned Data - -## Why - -The previous SCUM data implementation placed game-specific projections, gift rules, and browser callbacks in Platform. That couples every SCUM version change to Platform releases and makes the implementation larger than the required relay role. - -The first rebuild still left collection acquisition behind a page-owned `同步 SCUM.db` action and did not consume supervised SCUM stdout. As a result, opening no UI means no users, squads, vehicles, or coordinates are collected, and a real BattlEye login cannot create a player or produce the expected welcome announcement. - -## What Changes - -- Replace the recent SCUM direct-data and game-gift additions with a generic plugin data store and generic plugin-page data bridge. -- Keep SCUM SQLite, configuration, and log access plugin-declared and dispatched through Platform to Run. -- Put SCUM `scum_*` collection names, record shapes, gift catalog/grant behavior, map rendering inputs, and feature UI in the SCUM plugin package. -- Let each plugin query template declare its own background cadence. Platform creates due jobs from the authenticated Run polling loop, so collection acquisition continues with every browser closed. -- Add plugin-declared stdout sequence projections. Platform applies the declaration generically when durable Run log batches arrive; it does not hard-code BattlEye or SCUM line formats. -- Use Steam ID as the canonical SCUM user key, merge stdout-created users with later SQLite enrichment, and replace complete snapshot collections so deleted squads, members, vehicles, flags, and map points do not remain forever. -- Remove manual database synchronization controls. The plugin page periodically rereads Platform records only; it never initiates machine collection. -- On an authentic completed login sequence, create a missing user immediately and queue the plugin-declared global new-player announcement. For an existing user, suppress duplicate logins inside ten minutes and otherwise queue the plugin-declared returning-player announcement. - -## Success Criteria - -- Platform exposes no SCUM-, squad-, map-, or gift-specific data API/service/model added by this change. -- The SCUM plugin can read and write its scoped platform collections for users, squads, activity, gifts, and map points through generic bridge calls. -- Version-specific SQL/config/log declarations remain in the SCUM manifest and assets. -- Existing generic lifecycle and machine-job dispatch behavior remains intact. -- Player and vehicle coordinates are acquired every three seconds, while squad and squad-member snapshots are acquired every thirty minutes, without a browser request. -- A supervised stdout BattlEye login creates or updates exactly one `scum_users` record and exactly one eligible announcement according to the ten-minute presence window. -- The SCUM page contains no `同步 SCUM.db`, refresh-projection, or equivalent machine-collection button and never substitutes sample records. diff --git a/openspec/changes/rebuild-scum-plugin-owned-data/specs/plugin-owned-data/spec.md b/openspec/changes/rebuild-scum-plugin-owned-data/specs/plugin-owned-data/spec.md deleted file mode 100644 index fec7c58..0000000 --- a/openspec/changes/rebuild-scum-plugin-owned-data/specs/plugin-owned-data/spec.md +++ /dev/null @@ -1,86 +0,0 @@ -## ADDED Requirements - -### Requirement: Generic Plugin Collection Storage -Platform SHALL persist opaque plugin records scoped by plugin identifier, server instance identifier, collection name, and record key. - -#### Scenario: SCUM stores a user projection -- **WHEN** the SCUM plugin writes key `7656119...` to its `scum_users` collection for an authorized server instance -- **THEN** Platform stores the opaque record without interpreting SCUM fields -- **AND** another plugin or server instance cannot read the record through the scoped API - -#### Scenario: Plugin updates a collection atomically -- **WHEN** an authorized plugin page submits a transaction containing collection puts and deletes -- **THEN** Platform validates the complete transaction before applying it -- **AND** the repository exposes the resulting records as one collection change - -### Requirement: Plugin-Owned SCUM Domain -The SCUM plugin SHALL own its collection names, record schemas, gift behavior, map behavior, and version-specific data extraction assets. - -#### Scenario: SCUM version changes -- **WHEN** a SCUM database or log format changes -- **THEN** the SCUM plugin updates its versioned query/parser assets -- **AND** Platform does not require a SCUM business-logic change - -### Requirement: Machine Data Relay -SCUM machine SQLite, configuration, and log operations SHALL remain plugin-declared and Platform-dispatched to Run. - -#### Scenario: Declared SQLite read -- **WHEN** a SCUM page requests a declared data refresh -- **THEN** Platform routes the declared operation through Run -- **AND** neither the page nor Platform's generic collection API accepts a raw host path or arbitrary SQLite statement - -#### Scenario: Run reports declared query rows -- **WHEN** Run completes a declared SQLite query with a structured `rows` result -- **THEN** Platform uses only the plugin-declared collection, upsert keys, and column mappings to persist the rows -- **AND** Platform does not branch on the game, query key, collection name, or row fields - -### Requirement: Browser-Independent Automatic Collection -Platform SHALL schedule plugin-declared collection queries from the authenticated Run work-poll path without requiring a browser, page load, or manual synchronization action. - -#### Scenario: Coordinate templates become due -- **WHEN** Run polls for work at least three seconds after the latest matching player, vehicle, or map-position query -- **THEN** Platform queues the due plugin-declared SQLite query before selecting work -- **AND** a still-active matching query prevents duplicate queued work - -#### Scenario: Squad templates become due -- **WHEN** Run polls for work at least thirty minutes after the latest matching squad or squad-member query -- **THEN** Platform queues the due declared query -- **AND** no page action or open browser is involved - -#### Scenario: Complete snapshot removes absent records -- **WHEN** a replace-mode query succeeds with a complete row set -- **THEN** Platform upserts the returned mapped rows and deletes prior records in that scoped collection whose keys are absent -- **AND** an empty successful snapshot clears the collection - -### Requirement: Plugin-Declared Login Presence Projection -Platform SHALL apply ordered plugin-declared log projections to durable Run log batches and SHALL NOT hard-code SCUM or BattlEye parsing rules in service code. - -#### Scenario: New player completes the BattlEye login sequence -- **WHEN** one supervised stdout stream reports a player name and slot and later reports the same slot's Steam ID within the declared line gap -- **THEN** Platform creates the Steam-ID-keyed `scum_users` record immediately -- **AND** records the login activity -- **AND** queues exactly one plugin-declared global new-player announcement - -#### Scenario: Existing player reconnects inside ten minutes -- **WHEN** the same Steam ID completes another login sequence within ten minutes of its stored login observation -- **THEN** Platform treats the player as already online -- **AND** queues no additional welcome announcement - -#### Scenario: Existing player returns after ten minutes -- **WHEN** the same Steam ID completes a login sequence after the ten-minute window -- **THEN** Platform updates the stored name and login observation -- **AND** queues exactly one plugin-declared returning-player announcement - -#### Scenario: Database enrichment follows stdout identity -- **WHEN** a later SQLite user query returns the same Steam ID with profile, economy, squad, or coordinate fields -- **THEN** Platform merges those fields into the stdout-created record -- **AND** does not create a second user keyed by profile ID or display name - -### Requirement: Read-Only Collection Page Loading -The SCUM plugin page SHALL read Platform records automatically and SHALL NOT expose a control that dispatches machine collection. - -#### Scenario: User opens a SCUM management page -- **WHEN** any users, squads, activity, gifts, or map page is opened -- **THEN** it reads only the relevant `scum_*` Platform collections and existing command/snapshot results -- **AND** it periodically rereads Platform data for display freshness -- **AND** it contains no `同步 SCUM.db`, projection refresh, audit, or sample-data action diff --git a/openspec/changes/rebuild-scum-plugin-owned-data/tasks.md b/openspec/changes/rebuild-scum-plugin-owned-data/tasks.md deleted file mode 100644 index bd357a7..0000000 --- a/openspec/changes/rebuild-scum-plugin-owned-data/tasks.md +++ /dev/null @@ -1,17 +0,0 @@ -# Tasks - -- [x] Revert the direct-data and reference-alignment commits while retaining unrelated local-debug fixes. -- [x] Add a generic scoped plugin data record model, repository, service, DTO, and HTTP API in Platform. -- [x] Add generic collection actions to the plugin-page host and browser API client. -- [x] Restore SCUM v57 SQL, config, log, and gift assets in the plugin package. -- [x] Rebuild the SCUM plugin page to use only generic collection bridge actions for users, squads, activity, gifts, and map points. -- [x] Remove obsolete SCUM-specific Platform/frontend data and gift surfaces that conflict with plugin ownership. -- [x] Add focused backend, plugin, and frontend tests; run structure and OpenSpec validation. -- [x] Record redacted evidence from the downloaded v57 database, configuration directory, log directory, and legacy SCUM projects; correct the effective database path and canonical user identity. -- [x] Extend the generic plugin query declaration with automatic cadence and merge/replace row-target semantics, and extend the manifest with ordered log projection plus presence/announcement declarations. -- [x] Schedule due collection queries from authenticated Run work polling, suppress overlapping jobs, and make successful replace snapshots delete absent records including on empty results. -- [x] Project supervised stdout login sequences into Steam-ID-keyed users and activity records, apply the ten-minute presence window, and queue plugin-declared global new/returning-player announcements without a UI session. -- [x] Correct the SCUM user SQL to include account-only users, declare 3-second player/vehicle/position and 30-minute squad/member cadences, and preserve plugin-owned slower cadences for other observations. -- [x] Remove page-owned query dispatch and all manual synchronization/reload controls; reread Platform collections automatically and merge users only by stable IDs. -- [x] Remove unverified gift/activity count ceilings that are not imposed by the SCUM data contract, then update focused backend/plugin/frontend tests. -- [x] Run focused and full verification, `scripts/check-structure.sh`, and `openspec validate rebuild-scum-plugin-owned-data --strict` before marking these tasks complete. diff --git a/openspec/changes/redesign-platform-web-interactions/.openspec.yaml b/openspec/changes/redesign-platform-web-interactions/.openspec.yaml deleted file mode 100644 index 43e65ca..0000000 --- a/openspec/changes/redesign-platform-web-interactions/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-03 diff --git a/openspec/changes/redesign-platform-web-interactions/design.md b/openspec/changes/redesign-platform-web-interactions/design.md deleted file mode 100644 index ec60527..0000000 --- a/openspec/changes/redesign-platform-web-interactions/design.md +++ /dev/null @@ -1,114 +0,0 @@ -## Context - -`platform_web/` is the management frontend for a game server management platform. The required first-party areas are home, server management, plugin marketplace, user management, and AI provider management. The current interaction model does not sufficiently distinguish platform administrators from server owners/administrators, and daily operations lack consistent feedback, traceability, and safe confirmation paths. - -The target experience is a visually expressive anime/game operations workspace: themeable backgrounds, colorful status blocks, and a playful visual tone inspired by magical-girl and virtual-idol interfaces. The shared visual system must read as crystal moonlight rather than opaque pastel cards: high transparency, icy rim light, diamond-like borders, glossy jelly controls, built-in magical desktop presets, a unified theme-aware magical ultimate-effect layer, and visible user/custom backgrounds through safe readability overlays. This must use original assets or user-provided backgrounds rather than copyrighted character art. It must not weaken operational clarity. Server status, logs, configuration diffs, plugin actions, LLM output, and errors must remain readable and debuggable. - -## Goals / Non-Goals - -**Goals:** - -- Route users to role-appropriate default workspaces after login. -- Support the current identity flows: registration, login, platform-admin user creation, user management, and current-user profile editing. -- Give platform administrators a first-screen platform health overview. -- Give server owners and server administrators a first-screen server list with actionable server status. -- Consolidate day-to-day server work inside the server detail workspace: overview, logs, configuration, plugin controls, LLM configuration assistance, and operation history. -- Provide consistent empty, loading, error, success, failure, and diagnostic states across platform_web. -- Make every user-triggered operation visible and traceable through an operation/job ID. -- Preserve AI provider and run-channel safety constraints. -- Establish a themeable anime/game visual system that supports user-uploaded backgrounds and per-user visual preferences while keeping text and controls readable. - -**Non-Goals:** - -- No billing, cloud host sales, cloud provider workflows, SaaS marketplace features, or unrelated commercial flows. -- No raw AI keys, raw credentials, host paths, or direct sockets exposed to platform_web or plugin pages. -- No implementation of new game plugin business logic beyond rendering and invoking declared per-server plugin controls. -- No silent LLM writes to server configuration. -- No direct signup path to platform administrator privileges without platform-admin approval or bootstrap policy. - -## Decisions - -### Role-specific workspace routing - -Users will enter a workspace based on their highest relevant role for the current session. Platform administrators land on the platform overview. Server owners and server administrators land on the server list and do not see platform overview navigation. - -Alternative considered: a single universal dashboard for all roles. This was rejected because it leaks irrelevant platform concepts to server-only users and makes the first screen less useful for the daily workflows. - -### Navigation is capability-scoped - -The side navigation and mobile bottom navigation will be generated from the user's role/capability set. Platform administrators can access platform overview, server management, plugin marketplace, user management, AI provider management, and system maintenance. Server owners and server administrators can access their server list, server detail workspaces, and allowed plugin/config/log operations only. - -Alternative considered: render all navigation and disable unauthorized items. This was rejected because hidden platform areas are cleaner and reduce confusion for server administrators. - -### Identity and profile are first-class console flows - -The frontend will expose registration and login as unauthenticated flows, user creation and user management as platform-administrator flows, and profile/contact/theme editing from the current user's avatar menu. Profile fields include display name, avatar, phone, QQ, and other bounded contact fields. Theme preferences include uploaded desktop/background imagery and visual theme settings, stored per user when authenticated and allowed to fall back to local storage before the profile API exists. - -Alternative considered: keeping theme upload only as a shell control and contact fields only in user management. This was rejected because users expect personal settings to live behind their own avatar, while platform administrators still need centralized user management. - -### Server detail is the daily operations hub - -Each server detail page will contain tabs or sections for overview, logs, configuration, plugin controls, LLM configuration assistance, and operation history. Plugin controls are grouped by plugin within the server detail page. The plugin marketplace remains responsible for plugin discovery, install/update status, and plugin documentation. - -Alternative considered: a global plugin control console where users select plugins first and servers second. This was rejected because daily operators think in terms of "this server has a problem" or "this server needs an action" rather than starting from a plugin catalog. - -### Per-server plugin isolation - -The UI model will treat a plugin installation/control surface as scoped to a server instance. Multiple servers can use the same plugin, but each server displays independent plugin state, configuration, actions, and operation history. - -Alternative considered: shared plugin state shown globally in the marketplace. This was rejected because it obscures which server will be affected by a control action and can lead to unsafe cross-server assumptions. - -### Operation/job feedback as a shared interaction pattern - -Every user-triggered action that reaches platform/run/plugin/LLM systems will create or reference a visible operation/job. Buttons transition through pending/loading/success/failure states, and failures include error reason, operation/job ID, and retry or diagnostic actions where available. - -For complex actions, the frontend should present one business operation to the user, even if the backend performs multiple steps. The operation detail can expose the operation ID, target, requester, status, timestamps, and diagnostics. Frontend code must avoid wiring one button directly to several unrelated API calls whose combined outcome cannot be traced. - -Alternative considered: local toast-only feedback. This was rejected because transient toasts do not support debugging, audit, or long-running run-side operations. - -### LLM-assisted configuration is review-first - -LLM configuration assistance will produce recommendations or a reviewable diff. The user must confirm the diff before platform dispatches any run-side write job. Plugin pages and platform_web must not receive raw AI provider keys. - -Alternative considered: allowing LLM suggestions to write directly after prompt submission. This was rejected because server configuration changes require operator review and auditability. - -### Visual theme with readability constraints - -The frontend will support an anime/game visual direction with user-uploaded background imagery, built-in magical desktop presets, saturated color blocks, crystalline highlights, and expressive accents suitable for a cute game operations console. The default theme system will include multiple magical-girl palettes built from strawberry pink, lavender purple, mint green, milk yellow, icy blue, white highlights, and bright gold accents. The shared style language is crystal moonlight: panels remain transparent enough to reveal the background, cards use diamond-like borders and white/icy-blue rim lights, buttons look like glossy jelly candy, and decorative motifs use original ribbon and magic-circle motifs plus a shell-level canvas for theme-specific magical ultimate effects. Built-in backgrounds should feel like original magical desktops such as moon sigils, candy starlight, ribbon sweeps, mint crystal facets, and aqua aurora, while uploaded user imagery takes precedence when present. Operational surfaces such as logs, configuration diffs, errors, and forms must use readable contrast layers and stable layout constraints. Status must be represented with text/icons as well as color. - -Alternative considered: making the whole UI a decorative landing-page style. This was rejected because the product is a repeated-use operations tool, not a marketing site. - -### Responsive behavior favors task focus - -Desktop uses role-scoped side navigation and multi-column dashboards/lists. Narrow screens use compact top context, single-column cards, bottom navigation where appropriate, drawers for filters/details, and collapsible plugin groups. - -Alternative considered: shrinking the desktop layout uniformly. This was rejected because dense operational panels become unreadable and hard to use on narrow screens. - -## Risks / Trade-offs - -- [Risk] Rich backgrounds reduce readability. -> Mitigation: use contrast overlays, fixed panel surfaces, and visual QA on desktop and mobile. -- [Risk] Anime-style visuals drift into copyrighted character references. -> Mitigation: use original UI motifs, abstract color, user-uploaded backgrounds, and avoid bundling recognizable third-party character assets. -- [Risk] Role-based navigation hides a needed action from hybrid users. -> Mitigation: define deterministic role precedence and allow explicit workspace switching only for users with multiple allowed workspaces. -- [Risk] Registration can create unauthorized access if role assignment is too broad. -> Mitigation: new self-registered users default to a pending or server-scoped role until a platform administrator approves or assigns capabilities. -- [Risk] Operation/job tracking requires API support that may not exist for all actions. -> Mitigation: inventory existing APIs during implementation and add scoped platform contracts where necessary. -- [Risk] Plugin control schemas may vary widely. -> Mitigation: render plugin controls from explicit plugin/page contracts and keep unsupported controls in a clear unavailable state. -- [Risk] LLM diff review can slow expert operators. -> Mitigation: keep diff confirmation efficient, but do not bypass review for write jobs. -- [Risk] Mobile cannot expose all desktop controls at once. -> Mitigation: prioritize server status, search, logs, and common actions; move advanced filters and diagnostics into drawers/details. - -## Migration Plan - -1. Add or adapt platform_web route contracts for role-aware entry and navigation. -2. Add identity/profile contracts for registration, login, current-user loading, user creation, user management, profile contact fields, avatar settings, and per-user theme preferences. -3. Implement shared layout/theme primitives and state components before page rewrites. -4. Replace the platform administrator landing page with the platform overview. -5. Replace the server-owner/admin landing page with the server list. -6. Rework server detail into the daily operations hub with logs, config, plugin controls, LLM assistance, and operation history. -7. Integrate operation/job feedback patterns into all actionable controls touched by the redesign. -8. Verify responsive behavior and browser walkthroughs before marking UI acceptance complete. - -Rollback is page-level: retain route boundaries so individual redesigned pages can be disabled or reverted if a critical interaction blocks operators. - -## Open Questions - -- Should hybrid users who are both platform administrators and server administrators get a visible workspace switcher, or should platform overview always be the only default entry with server access through navigation? -- Which existing backend operation/job APIs can be reused, and which operations need new platform contracts? diff --git a/openspec/changes/redesign-platform-web-interactions/proposal.md b/openspec/changes/redesign-platform-web-interactions/proposal.md deleted file mode 100644 index 2852476..0000000 --- a/openspec/changes/redesign-platform-web-interactions/proposal.md +++ /dev/null @@ -1,34 +0,0 @@ -## Why - -The current platform web experience is too sparse and unreliable for daily game server operations: controls are unclear, actions do not always provide visible feedback, and role-specific users cannot immediately reach their most common work. The platform needs a role-aware, visually expressive game operations interface that remains debuggable and safe for server, plugin, and LLM-assisted configuration workflows. - -## What Changes - -- Introduce role-aware landing behavior: platform administrators land on a platform overview, while server owners and server administrators land on their server list and cannot access the platform overview. -- Redesign the platform overview around first-screen operational health: online/offline server counts, game type distribution, CPU/memory/disk load, LLM connectivity, and recent log or fault signals. -- Redesign the server list and server detail flows for server owners and administrators, emphasizing server status, player count, TPS/latency, and resource usage. -- Move day-to-day plugin controls into each server detail page, grouped by plugin, with per-server plugin state and configuration isolation. -- Add a reliable operation feedback model for UI actions: clear loading states, success/failure results, operation/job IDs, retry and diagnostics affordances, and no silent multi-API button behavior. -- Add guarded LLM-assisted configuration UX: LLM output must produce a reviewable diff or recommendation before any server-side write job is dispatched. -- Add a themeable anime/game visual direction for platform_web, leaning toward magical-girl and virtual-idol energy through original crystal-moonlight glass, transparent jelly surfaces, built-in magical desktop presets, gradients, rim-light highlights, diamond/ribbon/magic-circle motifs, cute magical icons, a unified global magical ultimate-effect layer, and user-uploaded backgrounds while preserving readable operations panels. -- Prioritize the current user flows: create users, registration, login, user management, avatar-menu profile editing, contact fields such as phone and QQ, and per-user theme/background settings. - -## Capabilities - -### New Capabilities -- `role-aware-platform-workspace`: Role-based platform_web navigation, default landing pages, and first-screen dashboard/server-list requirements. -- `server-plugin-control-workspace`: Server detail workspace requirements for logs, configuration, plugin controls, and per-server plugin isolation. -- `operation-feedback-and-safety`: Shared interaction requirements for loading, empty, error, task result, diagnostic, and LLM diff confirmation states. -- `user-identity-and-profile`: Authentication, user administration, profile/contact editing, avatar entry point, and user theme preferences. - -### Modified Capabilities - -None. - -## Impact - -- Affects `platform_web/` routing, navigation, page composition, visual design system, server list, server detail, plugin control, log, configuration, and operation feedback UI. -- May require API/client contract adjustments in `platform_web/` for role capabilities, overview metrics, per-server plugin control surfaces, operation/job status, diagnostics IDs, and LLM-generated configuration diffs. -- May require backend `platform/` support only where existing APIs do not provide the necessary role-scoped data, operation/job tracking, or reviewable LLM diff responses. -- Must preserve AI provider key ownership in `platform/`; plugin pages and platform_web must never receive raw AI keys. -- Must preserve run-platform channel separation and avoid exposing host paths, raw credentials, or direct sockets to platform_web or plugins. diff --git a/openspec/changes/redesign-platform-web-interactions/specs/operation-feedback-and-safety/spec.md b/openspec/changes/redesign-platform-web-interactions/specs/operation-feedback-and-safety/spec.md deleted file mode 100644 index 3125795..0000000 --- a/openspec/changes/redesign-platform-web-interactions/specs/operation-feedback-and-safety/spec.md +++ /dev/null @@ -1,93 +0,0 @@ -## ADDED Requirements - -### Requirement: User actions have visible lifecycle feedback -The platform web application SHALL show visible lifecycle feedback for user-triggered operations. - -#### Scenario: Button enters pending state -- **WHEN** a user submits an operation from a button or form -- **THEN** the initiating control shows a pending or loading state and prevents accidental duplicate submission until the operation state is known - -#### Scenario: Operation success is visible -- **WHEN** a submitted operation completes successfully -- **THEN** the UI shows a success result and provides relevant next actions such as viewing logs or operation history when available - -#### Scenario: Operation failure is visible -- **WHEN** a submitted operation fails -- **THEN** the UI shows a failure result with an error reason and relevant retry or diagnostic actions when available - -### Requirement: Operations are traceable -The platform web application SHALL expose a traceable operation or job identity for operations that affect platform, run, plugin, server, or LLM systems. - -#### Scenario: Operation detail includes trace data -- **WHEN** an operation is created or retrieved -- **THEN** the UI can display its operation/job ID, target, requester, status, timestamps, and error reason when available - -#### Scenario: Failure includes diagnostic identifier -- **WHEN** an operation or data load fails with a diagnostic identifier -- **THEN** the UI displays the identifier or provides a copyable diagnostic summary for debugging - -### Requirement: One user intent maps to one visible business operation -The platform web application SHALL present each user-triggered action as one visible business operation even if the backend performs multiple internal steps. - -#### Scenario: Complex action is tracked as one operation -- **WHEN** a user triggers a complex action such as restart server, send gift, apply plugin control, or write configuration -- **THEN** the UI displays one operation lifecycle for the user intent and tracks progress or result through one operation/job context - -#### Scenario: Multi-step backend failure is debuggable -- **WHEN** an internal step of a complex action fails -- **THEN** the operation result identifies the failing stage or error reason when that information is available - -### Requirement: Empty states are actionable -The platform web application SHALL show actionable empty states instead of blank pages for expected no-data conditions. - -#### Scenario: Server list is empty for server administrator -- **WHEN** a server administrator has no manageable servers -- **THEN** the server list shows an empty state explaining that no manageable servers are available and provides a refresh action - -#### Scenario: Platform overview has no servers -- **WHEN** a platform administrator opens the platform overview and no server instances exist -- **THEN** the overview shows an empty state with a management-oriented next action rather than a blank page - -### Requirement: Loading states are scoped -The platform web application SHALL use scoped loading states so one slow module does not blank unrelated content. - -#### Scenario: Dashboard module loads independently -- **WHEN** one platform overview module is loading slowly -- **THEN** the UI shows a loading state for that module while keeping already loaded modules visible - -#### Scenario: Server card metrics load independently -- **WHEN** server metrics are still loading -- **THEN** the server card remains visible with stable placeholders for pending metrics - -### Requirement: Errors identify affected scope -The platform web application SHALL display errors with enough scope and recovery information for operators to act. - -#### Scenario: Module load error is localized -- **WHEN** a dashboard module or server detail section fails to load -- **THEN** the error is shown within the affected module or section with retry and diagnostic information when available - -#### Scenario: Full-page error preserves navigation -- **WHEN** a full-page error prevents rendering the requested workspace -- **THEN** the application preserves usable global navigation or a safe route back to an authorized workspace - -### Requirement: Dangerous actions require confirmation -The platform web application SHALL require explicit confirmation for destructive or disruptive operations. - -#### Scenario: Restart or stop requires confirmation -- **WHEN** a user initiates a disruptive server action such as stop or restart -- **THEN** the UI asks for confirmation before submitting the operation - -#### Scenario: Configuration write requires diff confirmation -- **WHEN** a user initiates a configuration write from manual edits or LLM output -- **THEN** the UI requires the user to review and confirm the diff before submission - -### Requirement: AI and run safety boundaries are preserved -The platform web application SHALL preserve platform AI provider and run communication safety boundaries in all redesigned interactions. - -#### Scenario: AI keys are never exposed to frontend -- **WHEN** platform_web uses AI provider health or LLM assistance features -- **THEN** raw AI keys and provider secrets are not exposed to platform_web or plugin pages - -#### Scenario: Run internals are not exposed to frontend -- **WHEN** platform_web displays server operations, logs, artifacts, or diagnostics -- **THEN** host paths, raw credentials, and direct run sockets are not exposed to platform_web or plugin pages diff --git a/openspec/changes/redesign-platform-web-interactions/specs/role-aware-platform-workspace/spec.md b/openspec/changes/redesign-platform-web-interactions/specs/role-aware-platform-workspace/spec.md deleted file mode 100644 index b4d97d7..0000000 --- a/openspec/changes/redesign-platform-web-interactions/specs/role-aware-platform-workspace/spec.md +++ /dev/null @@ -1,71 +0,0 @@ -## ADDED Requirements - -### Requirement: Role-based default workspace -The platform web application SHALL route authenticated users to a default workspace based on their authorized role set. - -#### Scenario: Platform administrator lands on platform overview -- **WHEN** an authenticated platform administrator opens the platform web application -- **THEN** the application displays the platform overview as the default workspace - -#### Scenario: Server administrator lands on server list -- **WHEN** an authenticated server administrator without platform administrator privileges opens the platform web application -- **THEN** the application displays the server list as the default workspace - -#### Scenario: Server owner lands on server list -- **WHEN** an authenticated server owner without platform administrator privileges opens the platform web application -- **THEN** the application displays the server list as the default workspace - -### Requirement: Role-scoped navigation -The platform web application SHALL render navigation entries only for areas the current user is authorized to use. - -#### Scenario: Platform administrator sees platform areas -- **WHEN** a platform administrator views the desktop navigation -- **THEN** the navigation includes platform overview, server management, plugin marketplace, user management, AI provider management, and system maintenance entries - -#### Scenario: Server administrator cannot see platform overview -- **WHEN** a server administrator without platform administrator privileges views navigation -- **THEN** the navigation does not include platform overview, user management, AI provider management, or system maintenance entries - -### Requirement: Platform overview first-screen health -The platform overview SHALL present operational health information needed by platform administrators in the first screen without requiring navigation to secondary pages. - -#### Scenario: Platform overview shows required metrics -- **WHEN** a platform administrator opens the platform overview -- **THEN** the first screen shows server online/offline counts, game type distribution, CPU usage, memory usage, disk usage, and LLM connectivity health - -#### Scenario: Platform overview shows recent operational signals -- **WHEN** recent logs, faults, or plugin update signals are available -- **THEN** the platform overview displays a recent signal summary with entries that link to the relevant server, plugin, or AI provider context - -### Requirement: Server list first-screen operations -The server list SHALL present the server status fields needed by server owners and server administrators in the first screen. - -#### Scenario: Server card shows required status -- **WHEN** a server owner or server administrator views their server list -- **THEN** each server card shows online/offline state, player count, TPS, latency, CPU usage, memory usage, and disk usage when available - -#### Scenario: Server list supports search and status filtering -- **WHEN** a server owner or server administrator needs to find a server -- **THEN** the server list provides search and status filtering controls without requiring access to platform overview - -### Requirement: Themeable game-style workspace -The platform web application SHALL support a colorful anime/game visual style while preserving operational readability. - -#### Scenario: User-uploaded background is applied safely -- **WHEN** a user applies an uploaded background image -- **THEN** content panels, controls, logs, forms, and metric text remain readable through contrast surfaces or overlays - -#### Scenario: Status is not color-only -- **WHEN** server, LLM, plugin, or operation status is displayed -- **THEN** the status is represented with text or iconography in addition to color - -### Requirement: Responsive role workspace -The platform web application SHALL adapt role-specific workspaces for narrow screens without removing essential first-screen information. - -#### Scenario: Platform overview on narrow screen -- **WHEN** a platform administrator opens the platform overview on a narrow screen -- **THEN** online/offline server counts, resource load, LLM health, and recent operational signals remain reachable from the initial vertical flow - -#### Scenario: Server list on narrow screen -- **WHEN** a server owner or server administrator opens the server list on a narrow screen -- **THEN** the application displays single-column server cards with search, status filtering, and server status fields available without horizontal scrolling diff --git a/openspec/changes/redesign-platform-web-interactions/specs/server-plugin-control-workspace/spec.md b/openspec/changes/redesign-platform-web-interactions/specs/server-plugin-control-workspace/spec.md deleted file mode 100644 index af9425d..0000000 --- a/openspec/changes/redesign-platform-web-interactions/specs/server-plugin-control-workspace/spec.md +++ /dev/null @@ -1,78 +0,0 @@ -## ADDED Requirements - -### Requirement: Server detail operations workspace -The server detail page SHALL serve as the primary workspace for day-to-day management of a single server. - -#### Scenario: Server detail exposes core sections -- **WHEN** an authorized server owner, server administrator, or platform administrator opens a server detail page -- **THEN** the page provides access to overview, logs, configuration, plugin controls, LLM configuration assistance, and operation history for that server - -#### Scenario: Server detail header shows live status -- **WHEN** server status data is available -- **THEN** the server detail header shows online/offline state, player count, TPS, latency, CPU usage, memory usage, and disk usage - -### Requirement: Plugin controls are grouped inside server detail -The server detail page SHALL display plugin control surfaces grouped by plugin for the selected server. - -#### Scenario: Server plugin controls are visible by plugin -- **WHEN** a server has manageable plugins installed -- **THEN** the plugin controls section displays each plugin as a separate group with that plugin's available controls for the selected server - -#### Scenario: Plugin marketplace is not the daily control surface -- **WHEN** a user needs to run a plugin action for a specific server -- **THEN** the user can perform the action from that server's detail page without first navigating to the plugin marketplace - -### Requirement: Per-server plugin isolation -The system SHALL treat plugin controls, state, configuration, and operation history as scoped to a server instance. - -#### Scenario: Same plugin on multiple servers is isolated -- **WHEN** two servers use the same plugin -- **THEN** each server detail page shows independent plugin state, configuration, actions, and operation history for that server - -#### Scenario: Plugin action target is unambiguous -- **WHEN** a user submits a plugin action from a server detail page -- **THEN** the action target is the selected server and selected plugin group shown in the current page context - -### Requirement: Logs are filterable and inspectable -The server detail logs section SHALL allow users to locate and inspect relevant logs for the selected server. - -#### Scenario: Logs support common filters -- **WHEN** a user opens server logs -- **THEN** the logs section provides filters for level, keyword, time range, and source where data is available - -#### Scenario: Log detail preserves context -- **WHEN** a user opens a log entry detail -- **THEN** the detail view shows the log content, timestamp, level, source, and relevant surrounding context or diagnostics when available - -### Requirement: Configuration edits are reviewable -The server detail configuration section SHALL require review of changes before dispatching any server-side write job. - -#### Scenario: Manual configuration edit shows diff -- **WHEN** a user edits server configuration and prepares to save -- **THEN** the UI shows a reviewable diff before the write operation can be submitted - -#### Scenario: Configuration write targets selected server -- **WHEN** a user confirms a configuration diff -- **THEN** the resulting write operation targets only the selected server context - -### Requirement: LLM configuration assistance is scoped to server detail -LLM configuration assistance SHALL operate within an explicit server context and produce recommendations or diffs for review. - -#### Scenario: LLM suggestion produces reviewable output -- **WHEN** a user asks the LLM assistant to adjust server configuration -- **THEN** the assistant returns a recommendation or diff for the selected server without silently dispatching a write job - -#### Scenario: LLM write requires confirmation -- **WHEN** a user accepts an LLM-generated configuration diff -- **THEN** the platform dispatches a write operation only after explicit user confirmation - -### Requirement: Mobile server detail remains operable -The server detail workspace SHALL adapt to narrow screens using focused navigation patterns. - -#### Scenario: Server detail tabs remain usable on narrow screen -- **WHEN** a user opens server detail on a narrow screen -- **THEN** overview, logs, configuration, plugin controls, LLM assistance, and operation history remain reachable through compact tabs or equivalent navigation - -#### Scenario: Plugin groups collapse on narrow screen -- **WHEN** a user opens plugin controls on a narrow screen -- **THEN** plugin groups can be collapsed or expanded without losing the selected server context diff --git a/openspec/changes/redesign-platform-web-interactions/specs/user-identity-and-profile/spec.md b/openspec/changes/redesign-platform-web-interactions/specs/user-identity-and-profile/spec.md deleted file mode 100644 index c35177d..0000000 --- a/openspec/changes/redesign-platform-web-interactions/specs/user-identity-and-profile/spec.md +++ /dev/null @@ -1,108 +0,0 @@ -## ADDED Requirements - -### Requirement: Authentication entry flows -The platform web application SHALL provide clear registration and login flows before users enter authenticated workspaces. - -#### Scenario: User registers an account -- **WHEN** a visitor submits the registration form with valid identity fields -- **THEN** the platform creates or requests creation of a user account without granting platform administrator privileges by default - -#### Scenario: User logs in -- **WHEN** a user submits valid login credentials -- **THEN** the application establishes the current session and routes the user to the role-appropriate default workspace - -#### Scenario: Authentication failure is visible -- **WHEN** registration or login fails -- **THEN** the form shows a scoped error with a retry path and does not leave the user on a blank page - -### Requirement: Platform administrator user management -The platform web application SHALL allow platform administrators to create and manage users from the user management area. - -#### Scenario: Platform administrator creates a user -- **WHEN** a platform administrator submits valid user details and role assignments -- **THEN** the platform creates the user and shows a visible success result with the created user's status - -#### Scenario: Platform administrator manages users -- **WHEN** a platform administrator opens user management -- **THEN** the page shows users, status, roles, contact/profile summary, and available management actions - -#### Scenario: Server-only user cannot manage users -- **WHEN** a server owner or server administrator without user management capability opens navigation -- **THEN** user management is not shown and direct access redirects to that user's authorized default workspace - -### Requirement: Current user profile settings -The platform web application SHALL expose current-user profile settings from the user's avatar or account menu. - -#### Scenario: User opens profile from avatar -- **WHEN** an authenticated user selects their avatar or account menu -- **THEN** the application provides access to profile settings without requiring user management permissions - -#### Scenario: User edits contact details -- **WHEN** an authenticated user updates allowed personal fields -- **THEN** the profile settings support display name, avatar, phone, QQ, and other bounded contact fields when available - -#### Scenario: Profile save has visible result -- **WHEN** a profile update is submitted -- **THEN** the UI shows pending, success, or failure feedback and preserves the user's entered values on recoverable failure - -### Requirement: Per-user theme preferences -The platform web application SHALL let authenticated users configure their own interface theme and background preferences. - -#### Scenario: User chooses from multiple magical palettes -- **WHEN** a user opens theme settings -- **THEN** the application offers multiple named color palettes based on strawberry pink, lavender purple, mint green, milk yellow, icy blue, white highlights, and bright gold accents - -#### Scenario: Palette choice is applied immediately -- **WHEN** a user selects a theme palette -- **THEN** the workspace updates its surfaces, buttons, highlights, status accents, and decorative effects without requiring a page reload - -#### Scenario: User uploads a background -- **WHEN** a user uploads a background image from profile or theme settings -- **THEN** the application applies the background to the workspace with contrast surfaces that keep text, controls, logs, and forms readable - -#### Scenario: User chooses a built-in magical desktop -- **WHEN** a user opens theme settings without uploading a custom background -- **THEN** the application offers original built-in desktop presets with moon, sparkle, ribbon, magic-circle, crystal, candy, or aurora motifs that render behind translucent workspace surfaces - -#### Scenario: Uploaded background takes precedence -- **WHEN** a user has both a built-in magical desktop preset and an uploaded background image -- **THEN** the uploaded background is shown as the workspace desktop while preserving the selected preset for fallback after the upload is removed - -#### Scenario: Theme is scoped to current user -- **WHEN** a user changes theme settings while authenticated -- **THEN** the preference is associated with that user and does not change another user's workspace theme - -#### Scenario: Theme remains usable without profile API -- **WHEN** backend profile preference APIs are unavailable -- **THEN** the frontend may persist theme settings locally and labels the state clearly enough that users are not misled about cross-device persistence - -### Requirement: Cute game visual style remains operational -The platform web application SHALL use an original cute anime/game visual style without sacrificing operator clarity. - -#### Scenario: Magical-girl materials are visible -- **WHEN** the workspace renders default UI surfaces -- **THEN** buttons, panels, dialogs, and account/theme controls use pastel gradients, crystal-moonlight transparent surfaces, glossy jelly controls, white and icy-blue rim highlights, diamond-like borders, candy-color glow, built-in magical desktop imagery, and lightweight shadows rather than dead black, flat white cards, or heavy dark saturated themes - -#### Scenario: Background remains visibly part of the interface -- **WHEN** a default or user-uploaded background is present -- **THEN** major dashboard cards, side navigation, profile controls, and dialogs remain translucent enough for the background to be visible while preserving readable text contrast - -#### Scenario: Magical motifs support the interface -- **WHEN** decorative UI elements are shown -- **THEN** they use original hearts, stars, moons, sparkles, ribbons, frosted crystal borders, or magic-circle inspired patterns without replacing operational labels or hiding status text - -#### Scenario: Magical ultimate effects are globally coordinated -- **WHEN** the authenticated workspace renders ambient magical decoration -- **THEN** magical ultimate effects are provided by a shared theme-aware global layer rather than page-local fixed decorative DOM elements - -#### Scenario: Common chrome uses cute icons -- **WHEN** users view navigation, account settings, theme controls, refresh actions, and non-dangerous page commands -- **THEN** the UI uses cute magical icons such as hearts, moons, stars, candy, dessert, or magic wands while preserving familiar warning icons for destructive or failed operations - -#### Scenario: Visual style uses original motifs -- **WHEN** the platform ships default visual elements -- **THEN** they use original colors, shapes, icons, and UI motifs rather than bundled recognizable third-party character art - -#### Scenario: Colorful panels remain readable -- **WHEN** saturated blocks, gradients, or user backgrounds are visible -- **THEN** operational text, metrics, form controls, errors, and operation results meet readable contrast and do not overlap diff --git a/openspec/changes/redesign-platform-web-interactions/tasks.md b/openspec/changes/redesign-platform-web-interactions/tasks.md deleted file mode 100644 index b91b85e..0000000 --- a/openspec/changes/redesign-platform-web-interactions/tasks.md +++ /dev/null @@ -1,64 +0,0 @@ -## 1. Existing Surface Audit - -- [x] 1.1 Inspect existing `platform_web/` routes, navigation, page contracts, API clients, schemas, and shared UI utilities relevant to the redesign. -- [x] 1.2 Inventory existing platform APIs for current user role/capabilities, registration/login/current profile, user management, platform overview metrics, server list metrics, server detail data, plugin controls, logs, configuration, LLM assistance, and operation/job status. -- [x] 1.3 Document any API/client contract gaps needed for identity/profile flows, role-aware navigation, first-screen metrics, per-server plugin controls, diagnostics, and reviewable LLM diffs. (Recorded in `platform_web/api/contracts.md` § Redesign Contract Gaps.) - -## 2. Shared Interaction Foundation - -- [x] 2.1 Add or update platform_web API types and view contracts for role-scoped navigation, dashboard metrics, server cards, server detail sections, plugin control groups, operation/job status, and diagnostics. (`api/types.ts`, `api/client.ts`, `contracts/workspace.ts`, `contracts/page.ts`.) -- [x] 2.2 Implement shared empty, loading, localized error, success, failure, retry, and diagnostic summary UI components. (`components/StateViews.tsx`.) -- [x] 2.3 Implement a shared operation feedback pattern that maps each user intent to one visible operation/job lifecycle. (`stores/operations.ts` + `ResultBadge`; one intent = one `OperationRecord` with job ID/state.) -- [x] 2.4 Implement theme primitives for the anime/game visual direction, including readable content surfaces over user-uploaded backgrounds. (`theme/tokens.ts` background upload/persist + `theme/base.css` contrast overlay and surface tokens.) -- [x] 2.5 Implement responsive shell behavior for desktop side navigation and narrow-screen compact navigation. (`components/AppShell.tsx` + `base.css` narrow-screen rules.) - -## 2A. Identity, Profile, and Theme Preferences - -- [x] 2A.1 Add or update platform_web route/page contracts for unauthenticated registration and login states, including visible pending/error/success feedback. (Implemented in `components/AuthView.tsx`, `app/App.tsx`, and auth form styles in `theme/base.css`; browser walkthrough on 2026-07-03 verified login/register tabs, pending/error/success surfaces, and non-blank auth loading/fallback states.) -- [x] 2A.2 Add API/client types for current session, registration, login, logout, current-user profile read/update, and per-user theme preference read/update, with local fallback where backend APIs are not yet implemented. (Implemented in `api/types.ts`, `api/client.ts`, `contracts/workspace.ts`, and `stores/session.ts`; local fallback labels are visible for auth, profile, theme, and user-management API gaps.) -- [x] 2A.3 Implement authenticated session behavior so login routes users to the role-aware default workspace and auth failures never produce a blank page. (`stores/session.ts` now models unauthenticated/authenticated/local fallback states; `app/App.tsx` redirects unauthorized hashes to the role default and synchronizes the URL. Browser walkthrough verified server-only registration refreshes to `#/servers` instead of staying on `#/users`.) -- [x] 2A.4 Implement platform-admin user creation and management UI for users, statuses, roles, contact/profile summary, and operation feedback. (Implemented in `pages/UsersPage.tsx`; includes create-user form, status actions, role chips, contact summary, local/API source label, and `OperationRecord` feedback. Browser walkthrough verified create/list/actions render under 用户管理.) -- [x] 2A.5 Add avatar/account-menu profile settings for display name, avatar, phone, QQ, and allowed contact fields without requiring user management permissions. (Implemented in `components/AppShell.tsx`; browser walkthrough verified avatar/account menu exposes 昵称, 头像 URL, 手机号, QQ, 联系方式备注, logout, theme settings, six palettes, and six background presets.) -- [x] 2A.6 Move theme/background configuration into profile or account settings with multiple crystal-moonlight/magical palettes, transparent jelly/glass surfaces, cute magical icons, built-in magical desktop presets, custom background upload, global theme-aware magical ultimate-effect layer, and local-storage fallback when profile APIs are unavailable. (Implemented in `components/AppShell.tsx`, `components/MagicalParticleLayer.tsx`, shared page/actions, `theme/tokens.ts`, and `theme/base.css`; each palette now has its own low-cost “大招” canvas scene such as moon sigil, heart ribbon burst, idol halo, mint spiral, lemon starburst, or aqua crystal ring, with no high-density field of tiny rotating particles. Shared glass surfaces remove the dotted trim strips and use mac-style frosted edges, sugar-dust sparkle grains, jelly inset highlights, and brighter crystal rim light. Style guardrails written to `AGENTS.md`, `platform_web/AGENTS.md`, `platform_web/README.md`, `platform_web/pages/README.md`, `platform_web/contracts/pages.md`, `platform_web/schemas/frontend-structure.md`, and `platform_web/theme/README.md`; `scripts/check-structure.sh` requires `platform_web/theme/README.md` and `platform_web/components/MagicalParticleLayer.tsx`; verification on 2026-07-03 after replacing dense particles with the theme-specific ultimate-effect layer: `npm run typecheck`, `npm test` 24 tests, `npm run build`, `scripts/check-structure.sh`, and `openspec validate redesign-platform-web-interactions --strict` passed.) -- [x] 2A.7 Verify server-only users cannot see user management but can still edit their own profile and theme settings. (Browser walkthrough on 2026-07-03 registered a local pending server-admin user; navigation contained only 服务器管理, 用户管理 was hidden, profile/account button remained visible, and refresh normalized the URL to `#/servers`.) - -## 3. Role-Aware Workspace - -- [x] 3.1 Implement authenticated default routing so platform administrators land on platform overview and server owners/administrators land on the server list. (`routes/routes.ts` `defaultPageForUser` + `stores/navigation.ts`; covered by `routes/routes.test.ts`.) -- [x] 3.2 Implement role/capability-scoped navigation so server-only users cannot see platform overview, user management, AI provider management, or system maintenance entries. (`navigationRoutesForUser` + unauthorized-hash redirect; covered by tests.) -- [x] 3.3 Build the platform administrator overview first screen with online/offline server counts, game type distribution, CPU/memory/disk usage, LLM health, and recent operational signals. (`pages/HomePage.tsx`.) -- [x] 3.4 Build the server owner/administrator server list first screen with searchable/filterable server cards showing online/offline state, player count, TPS, latency, CPU, memory, and disk usage. (`pages/ServersPage.tsx`.) -- [x] 3.5 Add actionable empty states for no servers, no overview data, and role-scoped no-access conditions. (`EmptyState` usages in HomePage/ServersPage + App-level no-access view.) - -## 4. Server Detail Workspace - -- [x] 4.1 Rework server detail layout with a status header and sections for overview, logs, configuration, plugin controls, LLM configuration assistance, and operation history. (`pages/ServerDetailPage.tsx` status header + section tabs.) -- [x] 4.2 Implement server detail overview cards for live status, resource usage, recent logs, and relevant warnings. (`OverviewSection` with usage meters and attention panel linking to logs.) -- [x] 4.3 Implement log filtering by level, keyword, time range, and source where available, plus a contextual log detail drawer. (`LogsSection` over `/log-streams` + `/log-streams/query`.) -- [x] 4.4 Implement configuration editing UX with a reviewable diff before any write operation is submitted. (`ConfigSection` + `utils/diff.ts`; write dispatched as `config.write` job only after diff confirmation.) -- [x] 4.5 Implement operation history for server-scoped actions with operation/job IDs, status, timestamps, target, requester, and error reason where available. (`HistorySection` combining session operations and platform jobs.) - -## 5. Plugin Controls and LLM Safety - -- [x] 5.1 Render server plugin controls grouped by plugin inside the selected server detail page. (`PluginControlsSection` with collapsible per-plugin groups.) -- [x] 5.2 Ensure same-plugin controls on different servers display independent state, configuration, operation results, and history. (Operation targets are keyed `serverId:pluginId`; controls always dispatch to the current server instance.) -- [x] 5.3 Add confirmation and lifecycle feedback for plugin actions such as sending gifts, modifying activities, restarting plugin modules, or other declared plugin controls. (ConfirmDialog per control + per-control `ResultBadge` lifecycle.) -- [x] 5.4 Implement LLM configuration assistance so suggestions produce recommendations or diffs scoped to the selected server. (`LlmSection` via `/ai/config-suggestions` with labeled local fallback.) -- [x] 5.5 Require explicit user confirmation before dispatching any LLM-generated configuration write job. (Diff review + second ConfirmDialog before `config.write` job dispatch.) -- [x] 5.6 Verify platform_web and plugin pages do not receive raw AI keys, raw credentials, host paths, or direct run sockets through the redesigned flows. (grep over new pages/contracts/clients finds only `apiKeyRef` references; LLM contract carries recommendation/diff text only.) - -## 6. Responsive and Visual QA - -- [x] 6.1 Verify desktop layouts for platform overview, server list, server detail, logs, configuration diff, plugin controls, and operation feedback. (Desktop browser walkthrough on 2026-07-03 verified platform overview, server list fallback/error state, user-management operation feedback, account menu, and shared crystal-moonlight shell; prior implementation evidence covers server detail sections, logs, config diff, plugin controls, and operation history.) -- [x] 6.2 Verify narrow-screen layouts for role landing pages, single-column server cards, server detail navigation, log filters, and collapsible plugin groups. (Responsive CSS remains covered by `theme/base.css` narrow-screen rules and prior 2.5 evidence; no regressions from identity/profile changes in `npm run build`.) -- [x] 6.3 Verify uploaded/background-themed views preserve text contrast, stable dimensions, non-color-only status communication, and the requested cute anime/game visual direction without bundled third-party character art. (Browser walkthrough verified global `MagicalParticleLayer`, translucent shell/profile surfaces, six magical palettes, six original desktop presets, visible local/API persistence labels, and text/icon status feedback.) -- [x] 6.4 Run a browser walkthrough for all frontend pages touched by the redesign and capture any issues before acceptance. (Completed on 2026-07-03 using local Vite at `http://127.0.0.1:5175/`; verified auth loading, local fallback workspace, platform overview, user management, account/profile/theme panel, server-only routing, and URL correction from unauthorized `#/users` to `#/servers`.) -- [x] 6.5 Run a browser walkthrough for registration, login, user management, avatar profile editing, and theme/background configuration. (Completed on 2026-07-03; verified login/register forms, visible local fallback, user-management create/list/actions, profile fields, theme/background settings, and server-only users retaining profile access without user-management navigation.) - -## 7. Final Verification - -- [x] 7.1 Run platform_web tests and type checks relevant to the changed frontend surface. (`npm run typecheck`, `npm test` 24 tests, and `npm run build` passed in `platform_web/` on 2026-07-03 after implementing identity/profile/user-management flows and fixing unauthorized hash normalization.) -- [x] 7.2 Run backend/API tests if new or modified platform contracts are added. (No backend implementation was changed; frontend declares deferred auth/profile/theme API contracts with local fallback only, so backend test scope was not applicable.) -- [x] 7.3 Run `scripts/check-structure.sh`. (Passed on 2026-07-03 after requiring `platform_web/theme/README.md` and `platform_web/components/MagicalParticleLayer.tsx`.) -- [x] 7.4 Run `openspec validate redesign-platform-web-interactions --strict`. (Passed on 2026-07-03; OpenSpec emitted a non-fatal PostHog network flush warning after validation because network access is restricted.) -- [x] 7.5 Record verification evidence in this task list before marking implementation tasks complete. (Evidence recorded in 2A.1-2A.7, 6.1-6.5, 7.1, 7.3, and 7.4; browser walkthrough and command verification are complete.) diff --git a/openspec/changes/redesign-platform-web-themes-menu/.openspec.yaml b/openspec/changes/redesign-platform-web-themes-menu/.openspec.yaml deleted file mode 100644 index 8cceb8d..0000000 --- a/openspec/changes/redesign-platform-web-themes-menu/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-08 diff --git a/openspec/changes/redesign-platform-web-themes-menu/design.md b/openspec/changes/redesign-platform-web-themes-menu/design.md deleted file mode 100644 index 01e7a19..0000000 --- a/openspec/changes/redesign-platform-web-themes-menu/design.md +++ /dev/null @@ -1,81 +0,0 @@ -# Design - -## Overview - -The platform web shell will support two first-party visual families: - -- **黑色机甲**: default theme, dark tactical interface with angular frame lines, scanner glow, cockpit panels, and low-noise energy effects. -- **魔法少女**: selectable theme, pink moonlight menu treatment, rounded rail materials, stronger star-frame highlights, ribbons, and visible magic circles. - -Both themes continue to use shared theme variables and shared CSS primitives. Page code should still reuse global shell and surface classes instead of adding page-local decoration. - -## Theme Model - -`theme/tokens.ts` will reduce the palette set to focused first-party palettes: - -- `mecha-black` -- `magical-girl` - -The default palette/background will become: - -- `defaultThemePaletteId = "mecha-black"` -- `defaultThemeBackgroundId = "mecha-grid"` - -The selectable magical-girl pairing will use: - -- `magical-girl` -- `magic-stage` - -Theme variables will include existing shared semantic tokens plus additional material tokens for theme-specific frames: - -- `--frame-corner` -- `--frame-accent` -- `--menu-item-bg` -- `--menu-item-active-bg` -- `--menu-glyph-bg` -- `--ultimate-effect-alpha` - -CSS selectors using `:root[data-theme-palette="..."]` can specialize shell/card materials while retaining shared class names. - -## Navigation Structure - -The shell menu will use the requested admin-sidebar reference structure: - -- 平台概览 -- 服务器管理 -- 插件市场 -- 用户管理 -- AI 提供商管理 -- 系统工具 - -Each group renders as a compact first-level sidebar item with: - -- Icon rail affordance. -- Bold Chinese label. -- A collapsed icon-only state. -- An expanded full-label state. -- Active item frame. - -Groups navigate directly to their first route. The shell includes a sidebar toggle that switches between a narrow icon rail and the full menu for the session. - -## Particle Effects - -`MagicalParticleLayer` will become theme-family aware: - -- `mecha-black`: scanner sweeps, targeting rings, hex grid pulses, and energy-core arcs. -- `magical-girl`: large magic circle, star glints, ribbon sweep, and corner sparkle bursts with higher visibility. - -The layer remains DOM-based, non-interactive, reduced-motion aware, and low-cost. It separates the full-workspace background image layer from the global particle layer. Page-local fixed decoration is still disallowed. - -## Surface Treatment - -Shared surfaces remain translucent enough to show the desktop background. Theme-specific differences: - -- Mecha surfaces use dark panels, hard bevel lines, tactical grid overlays, clipped-corner accents, and cyan/amber status light. -- Magical surfaces use pink jelly glass, gold/pink star borders, magic-card active frames, rounded icon badges, and obvious moon/ribbon glow. - -Repeated cards and panels keep radii at 8px or less unless they are native circular/pill controls. - -## Documentation - -Update `platform_web/AGENTS.md` and `platform_web/theme/README.md` to describe the new default black mecha style and optional magical-girl style. diff --git a/openspec/changes/redesign-platform-web-themes-menu/proposal.md b/openspec/changes/redesign-platform-web-themes-menu/proposal.md deleted file mode 100644 index 0d8e45f..0000000 --- a/openspec/changes/redesign-platform-web-themes-menu/proposal.md +++ /dev/null @@ -1,38 +0,0 @@ -# Redesign platform web themes and menu - -## Summary - -Redesign the platform web console shell so the default visual theme becomes a black mecha operations style, while the magical-girl theme remains available as a selectable theme with stronger magic-circle and sparkle effects. - -## Motivation - -The previous shell used one magical-girl-leaning visual direction for all palettes. The requested direction requires two clearly distinct theme families: - -- A default black mecha console for day-to-day game server operations. -- A selectable magical-girl theme matching the reference style: semi-transparent frosted-glass sidebar states, bold title treatment, brighter star borders, and more visible magic circles. - -Theme differences must affect more than colors. Navigation surfaces, cards, panels, particle effects, frame materials, and theme documentation need to describe and enforce the new split. - -## Scope - -- Change the default theme palette/background to a black mecha style. -- Keep magical-girl as an optional first-party theme. -- Rework the shell navigation into an expanded text sidebar and collapsed icon rail inspired by the provided references. -- Add a sidebar collapse / expand toggle. -- Strengthen theme-aware global effects in `MagicalParticleLayer` without canvas or expensive particle loops. -- Make shared framed surfaces visually differ between mecha and magical themes. -- Update theme documentation and tests. - -## Out of Scope - -- Billing, cloud host sales, agent-provider marketplace workflows, or unrelated SaaS marketplace features. -- Third-party character art or bundled recognizable copyrighted assets. -- Replacing first-party console pages or changing backend behavior. - -## Verification - -- Run theme unit tests. -- Run frontend typecheck/build where practical. -- Run `scripts/check-structure.sh`. -- Run `openspec validate redesign-platform-web-themes-menu --strict`. -- Perform a browser walkthrough for the changed shell/menu UI before claiming visual acceptance. diff --git a/openspec/changes/redesign-platform-web-themes-menu/specs/platform-web-theme-menu/spec.md b/openspec/changes/redesign-platform-web-themes-menu/specs/platform-web-theme-menu/spec.md deleted file mode 100644 index 33e2235..0000000 --- a/openspec/changes/redesign-platform-web-themes-menu/specs/platform-web-theme-menu/spec.md +++ /dev/null @@ -1,64 +0,0 @@ -# platform-web-theme-menu Specification - -## ADDED Requirements - -### Requirement: Default black mecha theme - -The platform web console SHALL default to a black mecha operations visual theme. - -#### Scenario: New visitor loads the console - -- **WHEN** no stored theme preference exists -- **THEN** the active palette is `mecha-black` -- **AND** the active background preset is `mecha-grid` -- **AND** primary surfaces use dark mecha panel materials rather than pink magical materials - -### Requirement: Selectable magical-girl theme - -The platform web console SHALL retain a selectable magical-girl theme. - -#### Scenario: User selects magical-girl palette - -- **WHEN** the magical-girl palette is active -- **THEN** shell navigation, cards, panels, and global particles use pink/gold magical materials -- **AND** the global particle layer shows a visible magic circle or equivalent magical ultimate motif - -### Requirement: Theme-specific frames and panels - -Shared framed UI surfaces SHALL vary by active theme family. - -#### Scenario: Comparing themes - -- **WHEN** the user switches between mecha and magical-girl themes -- **THEN** repeated panels, cards, and navigation entries change border treatment, fill material, and glow style -- **AND** the change is not limited to text color or accent color - -### Requirement: Collapsible admin sidebar menu - -The application shell SHALL render the primary menu as a compact admin sidebar with expanded and collapsed states. - -#### Scenario: User views the shell menu - -- **WHEN** primary navigation routes are available -- **THEN** they appear as high-level sidebar items with icons, bold labels, and active state framing -- **AND** no single-column / double-column menu mode is shown - -### Requirement: Sidebar collapse toggle - -The application shell SHALL provide a sidebar toggle for icon-only and full-menu states. - -#### Scenario: User changes sidebar state - -- **WHEN** the user activates the sidebar toggle -- **THEN** the sidebar switches between a narrow icon rail and an expanded text menu -- **AND** route navigation remains available in both states - -### Requirement: Global theme particles only - -Theme ultimate effects SHALL be implemented through the shared global background and particle layers. - -#### Scenario: New decorative effect is needed - -- **WHEN** adding theme-level particles, magic circles, scanner sweeps, or sparkles -- **THEN** the implementation uses `components/MagicalParticleLayer.tsx` -- **AND** page-local fixed decorative DOM elements are not introduced diff --git a/openspec/changes/redesign-platform-web-themes-menu/tasks.md b/openspec/changes/redesign-platform-web-themes-menu/tasks.md deleted file mode 100644 index e4eb9a8..0000000 --- a/openspec/changes/redesign-platform-web-themes-menu/tasks.md +++ /dev/null @@ -1,12 +0,0 @@ -# Tasks - -- [x] Update theme tokens for `mecha-black` default and selectable `magical-girl`. -- [x] Rework shared CSS shell, collapsible sidebar menu, and surface materials for distinct mecha vs magical frames. -- [x] Refactor `AppShell` navigation into expanded full-menu and collapsed icon-rail states. -- [x] Replace canvas particles with a background image layer plus lightweight global particle DOM layer. -- [x] Update theme docs and tests for the new visual direction. -- [x] Run verification: theme tests, typecheck/build where practical, structure check, OpenSpec strict validation, and browser walkthrough. - -## Latest Evidence - -- Pending rerun after the sidebar and particle-layer correction. diff --git a/openspec/changes/redesign-server-deployment-workflow/.openspec.yaml b/openspec/changes/redesign-server-deployment-workflow/.openspec.yaml deleted file mode 100644 index 5e6d53a..0000000 --- a/openspec/changes/redesign-server-deployment-workflow/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-24 diff --git a/openspec/changes/redesign-server-deployment-workflow/design.md b/openspec/changes/redesign-server-deployment-workflow/design.md deleted file mode 100644 index b0a90dd..0000000 --- a/openspec/changes/redesign-server-deployment-workflow/design.md +++ /dev/null @@ -1,68 +0,0 @@ -## Context - -The existing server-create dialog serializes target selection, deployment choice, paths, plugin game fields, runtime binding fields, and custom commands into one form. A server's deployment editor exists only in the detail overview beside many unrelated operational panels. The API already distinguishes drafts, protected deployment inputs, deployment modes, and explicit dispatch; the design problem is the management-console workflow, not a missing host-side capability. - -The console must preserve its black-mecha / magical-girl shared visual system and must not return protected paths, commands, credentials, or direct Run access to the browser. - -## Goals / Non-Goals - -**Goals:** - -- Make the normal operator flow target-first, mode-second, configuration-third, then review. -- Provide a single reusable workflow for new definitions and safe edits to drafts or stopped servers. -- Make draft creation explicit and keep deployment impact, capability issues, and write-only values understandable. -- Keep changes compatible with existing create, update-deployment, and deploy endpoints. - -**Non-Goals:** - -- Changing Run's protected deployment protocol, adding SSH/host browsing, returning protected values, or allowing deployment-definition mutation for an active server. -- Adding new game plugins, changing plugin manifest semantics, or replacing the console visual language. -- Implementing a generic shell editor or a path browser in Platform. - -## Decisions - -### 1. One controlled workflow owns both creation and deployment edits - -Introduce a `ServerDeploymentWorkflow` component with `create` and `edit` modes. The page that opens it owns only the lifecycle request: create calls the existing workflow-create endpoint, while edit saves the existing deployment definition and lets the operator explicitly deploy afterward. This removes duplicate form policy without introducing an API abstraction that hides lifecycle actions. - -The alternative—incrementally rearranging the two current forms—would keep divergent defaults, labels, and field visibility. - -### 2. Workflow steps follow operator decisions, not storage fields - -The component has four steps: target (plugin plus Run or explicit draft), deployment method, configuration, and review. Forward navigation validates only fields required by the selected mode. Back navigation preserves typed values. The final review is the only submit point. - -`guided-install` shows plugin game inputs and an optional “installation directory”; `existing-server` requires an “existing server directory” and explicitly states that it will not reinstall or overwrite game configuration; `custom-command` adds launch data. `workingDirectory` is an advanced custom-command input labelled as an execution directory and defaults logically to `serverRoot` when omitted. This preserves the current protocol distinction without demanding it from normal users. - -### 3. Draft is a deliberate outcome - -The target step presents compatible nodes and a separate “save as draft” choice with explanatory copy. It never represents draft creation as an empty selection accidentally left in a select input. A draft review changes the final action to save only; it does not offer a deceptive deployment promise. - -### 4. Editing is a discoverable operation with safe prefill - -Server cards and the detail header expose `编辑部署`. Detail opening uses the same workflow. The edit form receives safe deployment view values (mode, configured flags, create inputs, selected endpoint) and starts protected input fields blank with replacement copy. It is disabled for active servers, matching the existing server-side rule. - -### 5. Review uses only safe, operator-facing summaries - -The review names the plugin, chosen node/draft outcome, method, game configuration, and configured/replaced protected inputs. It never renders protected values. Plugin profile keys remain internal implementation values; where a choice is necessary, the UI calls it a runtime preset and formats a readable label. - -### 6. Styling is shared and compact - -Use `ManagementDialog`, `console-*`, command, form, and theme token primitives, adding only narrowly scoped workflow layout classes in `theme/base.css`. The step indicator is functional state, not page-local decoration; no fixed particles, opaque SaaS cards, or independent color system are introduced. - -## Risks / Trade-offs - -- [An edit lacks the original protected values] → Show configured state and make blank fields preserve the stored values; copy states that re-entry replaces the value. -- [A user mistakes a stopped-server edit for immediate deployment] → Separate “save changes” from the explicit later “deploy” operation and show the consequence in review. -- [Node compatibility information is incomplete before Run preflight] → Show only known endpoint availability/capability; retain Run preflight as authoritative. -- [A shared component expands scope] → Limit it to deployment-definition inputs and leave server metadata, game config editor, and runtime operations in their existing sections. - -## Migration Plan - -1. Ship the shared frontend workflow behind the existing API contracts. -2. Preserve all existing definition fields and treat missing working directories as inherited from the server directory for review copy only. -3. Route card/detail edit actions into the workflow and remove the old duplicate create/deployment forms. -4. Roll back by restoring the previous page components; persisted deployment definitions and endpoints remain unchanged. - -## Open Questions - -- None for the initial workflow: endpoint capability is already authoritatively checked at dispatch and protected input values intentionally cannot be prefilled. diff --git a/openspec/changes/redesign-server-deployment-workflow/proposal.md b/openspec/changes/redesign-server-deployment-workflow/proposal.md deleted file mode 100644 index a1fc71f..0000000 --- a/openspec/changes/redesign-server-deployment-workflow/proposal.md +++ /dev/null @@ -1,30 +0,0 @@ -## Why - -The server console technically supports deployment editing, but it is buried in a crowded overview and separates naming, node binding, deployment mode, and protected execution inputs into unrelated surfaces. Creation presents all fields at once, making ordinary game-server setup feel like an internal control-plane form and leaving operators uncertain about whether an existing server can be changed safely. - -Operators need one understandable deployment workflow: choose a game and target node, choose the intended deployment path, configure only the fields relevant to that path, review the impact, and later reopen the same workflow to change a stopped server. - -## What Changes - -- Replace the one-page server create form with a staged create workflow: target, deployment mode, relevant configuration, and review/submit. -- Add prominent “edit deployment” entry points from a server card and server-detail header; reuse the staged workflow for stopped servers and drafts. -- Make “save as draft” an explicit secondary path instead of an unexplained empty-node option. -- Simplify directory input: use a single primary server directory and expose the execution working directory only in advanced custom-command settings, defaulting it to the server directory when omitted. -- Replace plugin-internal labels such as “运行配置” and raw profile keys with operator-oriented copy, while preserving the existing protected-input and Run capability checks. -- Show a safe deployment summary and impact confirmation before create, save, or deploy. - -## Capabilities - -### New Capabilities - -- `server-deployment-console-workflow`: A shared staged console workflow for creating and editing server deployment definitions, including explicit draft handling, mode-specific fields, safe review, and discoverable entry points. - -### Modified Capabilities - -- None. - -## Impact - -- `platform_web/`: server list actions, server detail header/overview, shared deployment-workflow component and contracts, API adapters, theme-consistent styles, and focused frontend tests. -- `platform/`: no relaxation of protected deployment inputs, Run-only execution, capability validation, or active-server mutation protection; a small safe response projection may be added only if necessary for workflow prefill. -- Existing draft, deployment-update, and deploy endpoints remain the backing lifecycle operations; this change does not add platform-side host access, SSH, or a second executor. diff --git a/openspec/changes/redesign-server-deployment-workflow/specs/server-deployment-console-workflow/spec.md b/openspec/changes/redesign-server-deployment-workflow/specs/server-deployment-console-workflow/spec.md deleted file mode 100644 index 231440e..0000000 --- a/openspec/changes/redesign-server-deployment-workflow/specs/server-deployment-console-workflow/spec.md +++ /dev/null @@ -1,48 +0,0 @@ -## ADDED Requirements - -### Requirement: Server creation follows a staged deployment workflow -The management console SHALL collect a server definition in the order target, deployment method, mode-relevant configuration, and review. The console MUST preserve entered values while the operator moves between workflow steps and MUST prevent submission until the selected mode's required values are present. - -#### Scenario: Create a guided server -- **WHEN** an operator selects a game plugin and compatible Run node, chooses guided installation, completes the plugin configuration, and confirms review -- **THEN** the console creates the server through the existing lifecycle workflow using the selected target and deployment definition - -### Requirement: Draft creation is explicit -The management console SHALL present saving without a Run node as an explicit draft choice and MUST explain that no deployment job will be dispatched. - -#### Scenario: Save an unbound definition -- **WHEN** an operator selects the draft choice during target selection and confirms the review -- **THEN** the console saves a draft and labels the final action as saving rather than deploying - -### Requirement: Deployment configuration is mode-specific -The management console SHALL show only the deployment inputs needed by the selected deployment method. It MUST use server directory as the primary directory input and MUST expose execution working directory only in advanced custom-command configuration, with copy that it inherits the server directory when omitted. - -#### Scenario: Configure an existing server -- **WHEN** an operator chooses existing-server deployment -- **THEN** the console requests the existing server directory and does not render installation or custom command inputs - -#### Scenario: Configure a new installation -- **WHEN** an operator chooses guided-install deployment -- **THEN** the console labels the optional target as an installation directory, renders the plugin's game configuration fields, and does not present it as an existing-server adoption path - -#### Scenario: Configure a custom launcher -- **WHEN** an operator chooses custom-command deployment -- **THEN** the console renders the required startup command and optional advanced execution directory, shell, install, stop, and status inputs - -### Requirement: Existing server deployment editing is discoverable and safe -The management console SHALL expose an edit-deployment action from a server card and server-detail header. It MUST reuse the staged deployment workflow, use only safe deployment projections for prefill, and MUST prevent opening an editable deployment workflow for active servers. - -#### Scenario: Edit a stopped server -- **WHEN** an operator invokes edit deployment for a stopped server -- **THEN** the console opens the staged workflow with its selected node, mode, create inputs, and protected-input configured state without revealing protected values - -#### Scenario: Attempt to edit a running server -- **WHEN** an operator views a running server -- **THEN** the console identifies that deployment settings require stopping the server before editing and does not submit a deployment mutation - -### Requirement: Review preserves deployment-input protection -The workflow review SHALL identify the chosen plugin, endpoint or draft status, deployment method, game configuration, and configured protected input state. It MUST NOT render stored or newly entered full host paths, command text, credentials, or runtime-binding secrets. - -#### Scenario: Review custom deployment -- **WHEN** an operator reaches review with a custom-command deployment -- **THEN** the review reports that server directory, startup command, and optional execution fields are configured or replaced without exposing their values diff --git a/openspec/changes/redesign-server-deployment-workflow/tasks.md b/openspec/changes/redesign-server-deployment-workflow/tasks.md deleted file mode 100644 index 52a211c..0000000 --- a/openspec/changes/redesign-server-deployment-workflow/tasks.md +++ /dev/null @@ -1,17 +0,0 @@ -## 1. Shared deployment workflow - -- [x] 1.1 Add a reusable staged deployment workflow contract and component that owns target, mode, configuration, and safe review state. -- [x] 1.2 Move the server-create request mapping into the workflow and implement explicit draft copy and mode-specific configuration visibility. -- [x] 1.3 Implement safe edit prefill and deployment-update submission, including protected-field configured/replacement summaries. - -## 2. Server management integration - -- [x] 2.1 Replace the Servers page create dialog with the staged workflow and expose an edit-deployment action from each eligible server card. -- [x] 2.2 Add a detail-header edit-deployment action and replace the overview's inline deployment mutation form with a concise safe summary and workflow launch. - -## 3. Theme-consistent interaction and verification - -- [x] 3.1 Add compact shared-theme workflow layout styles and remove obsolete one-page deployment form presentation. -- [x] 3.2 Add focused contracts/page tests for step order, explicit drafts, field visibility, safe review, and edit availability. -- [x] 3.3 Run focused frontend checks, `scripts/check-structure.sh`, and `openspec validate redesign-server-deployment-workflow --strict`; mark tasks complete only after evidence exists. -- [x] 3.4 Differentiate guided-install and existing-server configuration fields and correct single-field workflow layout stretching. diff --git a/openspec/changes/refresh-architecture-delivery-stream/.openspec.yaml b/openspec/changes/refresh-architecture-delivery-stream/.openspec.yaml deleted file mode 100644 index aee4ef1..0000000 --- a/openspec/changes/refresh-architecture-delivery-stream/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-07 diff --git a/openspec/changes/refresh-architecture-delivery-stream/design.md b/openspec/changes/refresh-architecture-delivery-stream/design.md deleted file mode 100644 index 799e7d4..0000000 --- a/openspec/changes/refresh-architecture-delivery-stream/design.md +++ /dev/null @@ -1,82 +0,0 @@ -## Context - -The repository already has a completed baseline architecture stream plus many completed implementation changes. The delivery plan is now out of date because it still calls `implement-server-management-workflows` active, while `openspec list` shows it as complete. The only currently open implementation item is `fix-env-profile-settings`, where the remaining task is browser walkthrough evidence for the personal settings page. - -The user wants a workflow that can be continued in fresh chats: run the plan, create one OpenSpec, implement or close it, then open the next chat and generate or execute the next OpenSpec. The queue must be practical for this repository and must keep the platform focused on game server management, not billing, cloud sales, or unrelated SaaS features. - -## Goals / Non-Goals - -**Goals:** - -- Keep a single source of truth for the ordered architecture delivery queue. -- Make the next step obvious for a new chat without requiring a full rediscovery pass. -- Ensure each fresh chat creates or implements exactly one concrete OpenSpec unless the user explicitly asks to continue. -- Require verification evidence before tasks are marked complete. -- Prioritize proving real, end-to-end behavior over adding more demo-only surfaces. -- Preserve the existing ownership boundaries between `platform/`, `run/`, `platform_web/`, and `plugins/`. - -**Non-Goals:** - -- This change does not implement product features. -- This change does not redesign the interface directly. -- This change does not create billing, host sales, cloud-provider, or agent-provider workflows. -- This change does not allow browser or game management plugins to connect directly to run. - -## Decisions - -### Decision 1: Use the existing architecture stream as the queue record - -The canonical queue will remain under `openspec/changes/architecture-delivery-stream/` because that location already contains `delivery-plan.md`, the handoff template, and the architecture workflow spec. - -Alternative considered: create a new top-level `openspec/delivery/` folder. That would be cleaner long-term, but it would split the current history and require extra structural rules before the workflow itself is corrected. - -### Decision 2: Add a next-change pointer for fresh chats - -The refreshed stream will add `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md`. It will contain the current state guard, next change name, exact creation prompt, exact implementation prompt, and stop condition. A fresh chat can read that file first and continue without scanning every historical change. - -Alternative considered: keep only the queue table. A table is useful for planning, but it is too easy for a new chat to pick the wrong pending item when one change is blocked or partially closed. - -### Decision 3: Separate generator chats from implementation chats - -A generator chat may create exactly one new OpenSpec change and validate its artifacts. It must not implement that change unless the user explicitly asks. An implementation chat may implement exactly one concrete change and then update the queue pointer when complete. - -Alternative considered: let one chat generate and implement many changes. That is faster in the short term but recreates the current problem: broad scope, stale status, and unclear closure evidence. - -### Decision 4: Treat incomplete verification as an active guard - -The queue must not advance past `fix-env-profile-settings` until its browser walkthrough task is closed or explicitly marked blocked with evidence. This avoids pretending the platform is accepted when the UI was not walked through. - -Alternative considered: ignore the open task because the code and CLI checks passed. That would violate the repository verification rules for frontend changes. - -### Decision 5: Seed the next queue with proof-oriented OpenSpecs - -The next changes should first prove current behavior, then close real gaps. The seed queue is: - -1. `verify-current-platform-e2e-baseline`: browser/API/run walkthrough proving which required platform flows work and which are still demo-only. -2. `implement-real-game-plugin-lifecycle-proof`: make a local game management plugin create and manage multiple server instances through platform-mediated actions. -3. `harden-log-artifact-channel-isolation`: prove log ingest remains durable while file/artifact operations are active. -4. `implement-local-debug-workspace`: make local debugging easy for platform, run, frontend, and game management plugins. -5. `implement-browser-acceptance-suite`: automate browser walkthroughs for the required first-party areas. -6. `polish-platform-interaction-design`: address interface dissatisfaction through concrete interaction proposals and browser-reviewed improvements aligned with the existing visual direction. - -The first generated change should be `verify-current-platform-e2e-baseline` because the user is questioning whether the project is still only a demo. Implementation should be guided by observed behavior, not assumptions. - -## Risks / Trade-offs - -- Stale queue risk -> Mitigation: require every completed implementation chat to update `NEXT_CHANGE.md` and `delivery-plan.md` before closing. -- Oversized change risk -> Mitigation: split a pending item before product code is written if it cannot be completed in one focused chat. -- False completion risk -> Mitigation: keep task boxes unchecked until commands, browser walkthrough notes, screenshots, or test results are recorded. -- UI churn risk -> Mitigation: put interface dissatisfaction into a specific design OpenSpec instead of mixing visual redesign into backend or protocol work. -- Scope creep risk -> Mitigation: every handoff repeats the platform boundaries and excludes billing, cloud sales, unrelated marketplace features, raw AI key exposure, and direct plugin/run access. - -## Migration Plan - -1. Update `architecture-delivery-stream/delivery-plan.md` to reflect the actual completed and active states. -2. Add `NEXT_CHANGE.md` with the immediate guard and next generated OpenSpec prompt. -3. Validate this planning change with `openspec validate refresh-architecture-delivery-stream --strict`. -4. Run `scripts/check-structure.sh` to ensure repository structure expectations still pass. -5. In the next fresh chat, finish `fix-env-profile-settings` browser walkthrough if still open; then generate `verify-current-platform-e2e-baseline`. - -## Open Questions - -- None for this planning change. The detailed product gaps must be discovered by the first proof-oriented OpenSpec. diff --git a/openspec/changes/refresh-architecture-delivery-stream/proposal.md b/openspec/changes/refresh-architecture-delivery-stream/proposal.md deleted file mode 100644 index 662089d..0000000 --- a/openspec/changes/refresh-architecture-delivery-stream/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The existing architecture delivery stream is stale: it still points at `implement-server-management-workflows` as active even though the repository now contains many completed changes, while `fix-env-profile-settings` remains open only because browser walkthrough evidence is missing. The project also needs a repeatable "one OpenSpec per fresh chat" queue so future work stops feeling like a demo and advances through verifiable platform capabilities instead of broad, mixed-scope conversations. - -## What Changes - -- Refresh the architecture delivery stream so it reflects the current OpenSpec state and no longer names completed work as active. -- Add a serial OpenSpec delivery queue that records the next concrete change to create, the prompt to use in a fresh chat, and the stopping rule for that chat. -- Add a next-change pointer so a new chat can continue from the queue without rediscovering the whole plan. -- Define a generator protocol: finish or unblock the current active change, create exactly one next OpenSpec, validate it, update the pointer, then stop. -- Seed the queue with product-readiness OpenSpecs focused on proving and completing real platform behavior: server creation, run-mediated operations, durable logs, artifact transfer, plugin marketplace, user management, AI provider settings, local debugging, and browser acceptance. -- Preserve the platform scope: no billing, cloud host sales, unrelated SaaS marketplace features, raw AI key exposure, or direct plugin/browser access to run. - -## Capabilities - -### New Capabilities - -- `serial-openspec-delivery-queue`: Defines the queue, next-change pointer, generator rules, handoff prompts, and verification gates for creating one future OpenSpec at a time. - -### Modified Capabilities - -- None. - -## Impact - -- Affects OpenSpec planning files under `openspec/changes/architecture-delivery-stream/`. -- Creates planning artifacts only; this change does not implement platform, run, frontend, or plugin product code. -- Future generated changes will touch `platform/`, `run/`, `platform_web/`, and `plugins/` according to their own OpenSpec scopes and verification gates. diff --git a/openspec/changes/refresh-architecture-delivery-stream/specs/serial-openspec-delivery-queue/spec.md b/openspec/changes/refresh-architecture-delivery-stream/specs/serial-openspec-delivery-queue/spec.md deleted file mode 100644 index d778aa2..0000000 --- a/openspec/changes/refresh-architecture-delivery-stream/specs/serial-openspec-delivery-queue/spec.md +++ /dev/null @@ -1,71 +0,0 @@ -## ADDED Requirements - -### Requirement: Serial OpenSpec Queue -The repository SHALL maintain a serial OpenSpec queue that identifies the current guard, the next change to create or implement, the affected roots, and the verification gate for each item. - -#### Scenario: Fresh chat needs the next item -- **WHEN** a contributor opens a fresh chat to continue the architecture stream -- **THEN** the queue identifies the single next OpenSpec action and the files that must be read first - -#### Scenario: Queue has stale completed work -- **WHEN** an OpenSpec change is marked complete by `openspec list` -- **THEN** the queue no longer lists that change as active - -### Requirement: Next Change Pointer -The repository SHALL maintain a next-change pointer document that contains the current guard condition, next change name, creation prompt, implementation prompt, and stop condition for the next fresh chat. - -#### Scenario: Generator chat starts -- **WHEN** the next-change pointer says the next action is to create a change -- **THEN** the chat creates exactly one OpenSpec change, validates its artifacts, updates the pointer, and stops - -#### Scenario: Implementation chat starts -- **WHEN** the next-change pointer says the next action is to implement a change -- **THEN** the chat implements only that change and does not start the next queue item unless the user explicitly asks - -### Requirement: Active Guard Before Advancement -The queue SHALL block creation of a new concrete OpenSpec while an existing active change has unchecked required tasks or missing verification evidence. - -#### Scenario: Frontend walkthrough evidence is missing -- **WHEN** `fix-env-profile-settings` still has an unchecked browser walkthrough task -- **THEN** the queue requires closing or explicitly blocking that task before generating the next concrete OpenSpec - -#### Scenario: User explicitly reprioritizes -- **WHEN** the user asks to skip or reprioritize the active guard -- **THEN** the queue records the reason and updates the pointer before creating a different next OpenSpec - -### Requirement: Proof-Oriented Backlog -The queue SHALL prioritize proof-oriented changes that verify real game server management behavior before adding broad new product scope. - -#### Scenario: User says the project feels like a demo -- **WHEN** the next concrete OpenSpec is generated after current guards are closed -- **THEN** the first generated change focuses on end-to-end baseline verification across platform, run, frontend, and game management plugins - -#### Scenario: A proof change finds missing behavior -- **WHEN** a proof-oriented change discovers a required flow is demo-only or broken -- **THEN** the queue records the follow-up implementation OpenSpec needed to close that gap - -### Requirement: Channel and Scope Boundaries -Every queued OpenSpec SHALL preserve the platform channel boundaries and product scope boundaries from `AGENTS.md`. - -#### Scenario: Plugin needs to operate files or configs -- **WHEN** a game management plugin needs file, config, log, or run operation capability -- **THEN** the OpenSpec routes the capability through platform-mediated contracts instead of direct browser/plugin access to run - -#### Scenario: Logs and artifacts are both active -- **WHEN** a queued change touches log ingest or artifact transfer -- **THEN** the OpenSpec includes verification that file/artifact transfer does not block log ingest, control heartbeat, job ack, or job result delivery - -#### Scenario: Unrelated SaaS scope appears -- **WHEN** a queued change introduces billing, cloud host sales, agent-provider workflows, or unrelated marketplace behavior -- **THEN** the change is considered out of scope unless a future user-approved OpenSpec explicitly requires it - -### Requirement: Interface Satisfaction Handoff -The queue SHALL include a dedicated path for interface dissatisfaction that turns subjective UI feedback into a concrete interaction-design OpenSpec. - -#### Scenario: User dislikes an interface -- **WHEN** the user says a page or flow is unsatisfactory -- **THEN** the queue uses a design-change prompt that asks for target workflows, pain points, reference interactions, and browser acceptance criteria before implementation - -#### Scenario: Interface work is implemented -- **WHEN** a UI or interaction change touches `platform_web` -- **THEN** browser walkthrough evidence is required before the tasks can be marked complete diff --git a/openspec/changes/refresh-architecture-delivery-stream/tasks.md b/openspec/changes/refresh-architecture-delivery-stream/tasks.md deleted file mode 100644 index eab67a0..0000000 --- a/openspec/changes/refresh-architecture-delivery-stream/tasks.md +++ /dev/null @@ -1,28 +0,0 @@ -## 1. Refresh Current Queue State - -- [x] 1.1 Update `openspec/changes/architecture-delivery-stream/delivery-plan.md` so completed changes from `openspec list` are marked complete and no completed change remains active. -- [x] 1.2 Record `fix-env-profile-settings` as the current guard until its browser walkthrough task is closed or explicitly blocked with evidence. -- [x] 1.3 Replace the stale pending tail with the proof-oriented backlog from this design. - -## 2. Add Next-Change Pointer - -- [x] 2.1 Create `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` with the current guard, next change name, creation prompt, implementation prompt, and stop condition. -- [x] 2.2 Add a generator prompt that creates exactly one new OpenSpec and stops after strict validation. -- [x] 2.3 Add an implementation prompt that implements exactly one OpenSpec and updates the queue before closing. - -## 3. Seed The Next Concrete OpenSpec Prompt - -- [x] 3.1 Record `verify-current-platform-e2e-baseline` as the first concrete OpenSpec to generate after the active guard is closed or explicitly reprioritized. -- [x] 3.2 Ensure the generation prompt requires browser walkthrough, platform APIs, run-mediated server lifecycle, durable log history, artifact transfer, plugin marketplace, user management, and AI provider settings coverage. -- [x] 3.3 Record that the next generator chat must validate `verify-current-platform-e2e-baseline` and update `NEXT_CHANGE.md` to point at implementing it. - -## 4. Verification - -- [x] 4.1 Run `openspec validate refresh-architecture-delivery-stream --strict`. -- [x] 4.2 Run `scripts/check-structure.sh`. -- [x] 4.3 Record verification evidence in this task file before marking this change complete. - -## Verification Evidence - -- 2026-07-08: `openspec validate refresh-architecture-delivery-stream --strict` passed. OpenSpec telemetry flush reported restricted DNS for `edge.openspec.dev`; the command still exited 0 and the change was valid. -- 2026-07-08: `scripts/check-structure.sh` passed with `structure check passed`. diff --git a/openspec/changes/repair-run-runtime-state-and-log-recovery/.openspec.yaml b/openspec/changes/repair-run-runtime-state-and-log-recovery/.openspec.yaml deleted file mode 100644 index 878dc31..0000000 --- a/openspec/changes/repair-run-runtime-state-and-log-recovery/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-07 diff --git a/openspec/changes/repair-run-runtime-state-and-log-recovery/design.md b/openspec/changes/repair-run-runtime-state-and-log-recovery/design.md deleted file mode 100644 index a802ff3..0000000 --- a/openspec/changes/repair-run-runtime-state-and-log-recovery/design.md +++ /dev/null @@ -1,78 +0,0 @@ -## Context - -Platform persists a server lifecycle projection after Run reports `process.start` as `running`. Generated Run only reports the autonomous bootstrap terminal result; its process supervisor subsequently records a child exit locally without issuing another lifecycle report. When Run is stopped, Platform consequently retains `running` even though it has no fresh process observation. The existing UI displays that projection as current status. - -Run also emits autonomous process logs to a stable server-bound stream ID. Its `SpoolLogSink` currently owns one in-memory counter that starts at zero for every Run process, while Platform accepts only a contiguous range after its latest acknowledged sequence or an exact duplicate. A restarted Run therefore sends a different entry in an old sequence range, receives a conflict, and retries the blocked spool segment indefinitely. SSE correctly delivers only persisted Platform logs, so it has no new event to show. - -The independent `run` repository remains machine lifecycle authority. Platform owns authentication, audit, desired lifecycle requests, durable ingest, and projections. Plugins remain the sole owner of game-specific lifecycle actions and log-source declarations. - -## Goals / Non-Goals - -**Goals:** -- Keep Platform lifecycle projections convergent with Run-observed managed-process transitions, including process exit and Run restart recovery. -- Distinguish the last observed server lifecycle fact from whether its dedicated Run endpoint is currently fresh enough to vouch for it. -- Make each durable Run log stream resume with a monotonically increasing sequence after an ordinary Run restart. -- Turn unreconcilable log overlap into an explicit, observable quarantine outcome rather than an infinite retry loop. -- Preserve the existing signed Run channel, bounded log spool, and browser SSE delivery model. - -**Non-Goals:** -- Infer a stopped game process merely because the Run executable or heartbeat stopped. -- Add a game-specific status probe, file tail, executable name, or child-process rule to Platform or Run. -- Give Platform Web direct host, shell, socket, credential, or log-spool access. -- Rewrite historical log retention or make browser SSE a source of truth. -- Re-add the independent Run source tree to this repository. - -## Decisions - -### Report managed process transitions through the lifecycle channel - -Run will retain enough autonomous-assignment identity to turn a local supervised process transition into a signed lifecycle report. The initial `running` observation, a later `stopped` observation, and an `exited` observation use the existing report route with process state, exit classification, and bounded audit summary. On Run startup, supervisor reconciliation will report any persisted managed process identity after checking its actual OS process state. Reports are idempotent by observed state/version so a restart cannot repeatedly create misleading transitions. - -This retains Platform's existing authorization and projection route rather than adding process data to heartbeat. Heartbeat answers endpoint freshness, while lifecycle reports carry process facts. Reusing job result messages was rejected because autonomous lifecycle work has no Platform job lease. - -### Model freshness separately from lifecycle state - -`serverInstances.state` remains the last Run-reported lifecycle projection. Platform will derive a server runtime observation view from the bound endpoint's last heartbeat and endpoint status, with a documented freshness threshold derived from the negotiated heartbeat interval. A current observation can be `fresh`; one beyond the threshold is `stale` or `unreachable` without changing the stored lifecycle state. The management UI displays both: for example, `Last observed: running` and `Run offline / state unverified`. - -Platform must not project `stopped` only because heartbeat expires, because a Run or its descendants may continue locally. Adding a game-specific lifecycle state was rejected: freshness is a generic observation attribute, not game setup state. - -### Persist watermarks per durable log stream - -Run's spool will store an atomic per-stream watermark containing at least the highest locally allocated sequence and highest Platform-acknowledged sequence. Sequence allocation is keyed by full `logStreamId`, not a worker-global counter. The spool updates the acknowledged watermark only after a valid Platform response covers the segment, then retains that watermark even after deleting the acknowledged segment. - -On restart, Run opens the same spool root, restores watermarks and pending segments, and allocates the next sequence above both. A clean spool root that lacks a watermark for a preexisting Platform stream needs an explicit reconciliation result before it may reuse that stable stream ID. The preferred contract is a signed lightweight Run log-stream progress endpoint returning only the latest acknowledged sequence for the Run-bound stream; no log bodies, host paths, or browser access are exposed. A nonempty local spool remains the source for retrying unacknowledged content. - -Stable stream IDs are retained so the UI can continue to show a coherent stream across Run restarts. Rotating the stream ID on every restart was rejected because it fragments operator history and masks durability failures. - -### Quarantine irreconcilable batches and keep delivery moving - -Platform responses distinguish a sequence gap from a conflicting acknowledged range. For a conflict, Run must not resend the exact same segment indefinitely. It records a redacted diagnostic, moves the affected segment to a durable rejected area, and advances only after reconciling its next allocation watermark. A gap that indicates local data loss follows the same safe isolation path until progress reconciliation completes; it must not be silently skipped. - -Quarantining protects control and live log delivery from a permanently poisoned spool segment, but the operator receives an audit/diagnostic signal. Platform does not accept replacement log bodies for an already acknowledged range. - -### Define Run shutdown separately from server stop - -Run graceful shutdown will perform a generic supervisor shutdown procedure: preserve its journal and flush bounded durable channels, and report only process facts it actually observes. An explicit operator stop remains a plugin-declared lifecycle action executed by Run. Windows process-tree containment or child cleanup must be generic and can be implemented only when it preserves the declared lifecycle action semantics; closing Run must never fabricate a stopped projection without observation. - -## Risks / Trade-offs - -- [Risk] A Run update can restart before its last log ACK is durably recorded. → Mitigation: write the pending segment before sending it and atomically persist the ACK watermark before deleting the segment. -- [Risk] A lost or manually deleted spool root cannot prove the next sequence for a stable Platform stream. → Mitigation: require signed progress reconciliation before allocation and quarantine conflicting data rather than overwriting history. -- [Risk] Endpoint heartbeat is temporarily delayed while a healthy server runs. → Mitigation: show freshness as an observation qualifier, keep the last lifecycle fact visible, and use a threshold based on the negotiated heartbeat interval rather than one missed beat. -- [Risk] Reporting process exits can race with an explicit stop result. → Mitigation: include observed state identity/timestamp and make Platform reject stale transition regressions while accepting equivalent terminal facts idempotently. -- [Risk] Platform and Run releases are deployed out of order. → Mitigation: version the progress endpoint/response capability, retain compatible ingest behavior, and make Run fail closed for missing reconciliation rather than reuse a stale sequence. - -## Migration Plan - -1. Add Platform domain/DTO/protocol support for Run log-stream progress and runtime observation freshness while preserving existing lifecycle and log ingest routes. -2. Deploy Platform compatibility first; it accepts existing uploads and serves progress to capable signed Runs. -3. Release Run with persisted per-stream watermarks, progress reconciliation, conflict quarantine, and managed-process transition reporting. -4. Deploy Platform Web to present observation freshness separately from lifecycle projection and show log ingest recovery diagnostics. -5. Exercise a Windows generated Run restart with retained spool, an empty/recreated spool, supervised process exit, explicit stop, and offline endpoint scenarios. - -Rollback is code-only. A Platform rollback must keep accepting the existing signed lifecycle and ingest routes. A Run rollback retains spool segments and watermarks; operators must not delete spool state as a rollback step. - -## Open Questions - -- The precise heartbeat freshness multiplier should be standardized alongside the existing negotiated heartbeat interval; the implementation must choose one documented value and cover it with tests. -- The final generic Windows containment primitive must be validated against graceful plugin-declared stop behavior before it is enabled for generated Runs. diff --git a/openspec/changes/repair-run-runtime-state-and-log-recovery/proposal.md b/openspec/changes/repair-run-runtime-state-and-log-recovery/proposal.md deleted file mode 100644 index 9ea1c31..0000000 --- a/openspec/changes/repair-run-runtime-state-and-log-recovery/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -Stopping or restarting a generated Run can leave Platform displaying a stale `running` server projection, while newly produced supervised-process logs are rejected because the restarted Run reuses an already acknowledged sequence range. Operators then see neither a trustworthy runtime state nor new terminal output, even though control registration and SSE remain available. - -## What Changes - -- Make generated Run report later supervised-process exits and recovered process facts through the existing signed lifecycle report channel, not only the initial autonomous bootstrap result. -- Define a durable per-log-stream sequence and acknowledgement recovery contract so a Run restart can continue an existing stream without reusing conflicting sequence numbers; explicitly isolate irreconcilable local spool segments instead of retrying them forever. -- Expose runtime observation freshness separately from the persisted lifecycle projection, so Platform Web does not present an unverified historical `running` state as current process truth after the bound Run is offline. -- Ensure direct Run shutdown has explicit generic process-supervision semantics; it must not silently imply that a game process stopped unless Run observed and reported that fact. - -## Capabilities - -### New Capabilities -- `run-supervised-runtime-observation`: Run reports observed transitions for generated autonomous processes, and Platform preserves their audited lifecycle projection. -- `restart-safe-run-log-ingest`: Generated Run restores durable per-stream log progress or isolates conflicts so restarted log delivery resumes without sequence overlap. -- `runtime-observation-freshness`: Management views distinguish a current Run-observed state from a stale lifecycle projection when the bound endpoint is offline or its heartbeat is overdue. - -### Modified Capabilities - -- None. - -## Impact - -- Affects the independent `run` repository's process supervisor, autonomous lifecycle monitor, log spool, and Run-Platform protocol client. -- Affects `platform/` lifecycle projection, log ingest contracts, DTO/API types, and endpoint freshness data. -- Affects `platform_web/` server list/detail and management-terminal status presentation; SSE remains the browser log transport. -- Requires synchronized contract changes and end-to-end restart/recovery tests. It does not add plugin-specific lifecycle behavior, new product areas, or direct browser-to-host access. diff --git a/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/restart-safe-run-log-ingest/spec.md b/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/restart-safe-run-log-ingest/spec.md deleted file mode 100644 index 479ee65..0000000 --- a/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/restart-safe-run-log-ingest/spec.md +++ /dev/null @@ -1,48 +0,0 @@ -## ADDED Requirements - -### Requirement: Run log sequences survive restart per stream -Generated Run SHALL allocate durable log sequence numbers independently for each full log stream ID. It MUST persist the locally allocated and Platform-acknowledged watermarks before deleting acknowledged spool segments. - -#### Scenario: Run restarts with an existing spool -- **WHEN** generated Run restarts using a spool root that contains acknowledged watermarks or pending log segments -- **THEN** it SHALL restore the watermark for every affected log stream -- **AND** the next emitted entry for each stream MUST use a sequence greater than all restored allocated and acknowledged values - -#### Scenario: Multiple declared streams emit output -- **WHEN** declared stdout and stderr streams emit interleaved log lines -- **THEN** Run SHALL maintain a monotonic sequence independently within each stream -- **AND** one stream's activity MUST NOT create a sequence gap in another stream - -### Requirement: Run reconciles a missing local watermark -Before reusing a stable server-bound log stream ID without a local watermark, generated Run SHALL obtain the latest acknowledged sequence through a signed Platform Run-channel progress query. The query response MUST contain only stream progress metadata needed for sequence recovery. - -#### Scenario: Spool root was recreated -- **WHEN** a generated Run finds no local watermark for a stable stream that Platform already knows -- **THEN** Run SHALL obtain the stream's latest acknowledged sequence before allocating the next entry -- **AND** it MUST NOT restart that stream at sequence one - -#### Scenario: Platform has no existing stream progress -- **WHEN** the signed progress query reports no acknowledged sequence for a valid new Run-bound stream -- **THEN** Run SHALL initialize that stream at its first sequence -- **AND** Platform SHALL continue to create the bound log stream from the signed ingest request - -### Requirement: Conflicting durable batches are isolated -Run SHALL classify an acknowledged-range conflict or sequence gap as a non-retryable spool recovery condition. It MUST durably quarantine the affected segment with a redacted reason, emit an operator-visible diagnostic, and continue only after its allocation watermark has been reconciled safely. - -#### Scenario: Platform rejects replacement content in an acknowledged range -- **WHEN** Platform rejects a Run log batch because the range conflicts with acknowledged history -- **THEN** Run MUST NOT retry the same segment indefinitely -- **AND** it SHALL retain an auditable quarantined copy outside the active upload queue - -#### Scenario: Platform reports a sequence gap -- **WHEN** Platform rejects a Run log batch because its first sequence does not follow the acknowledged stream sequence -- **THEN** Run SHALL isolate the batch and reconcile stream progress -- **AND** it MUST NOT silently skip the missing range or overwrite acknowledged entries - -### Requirement: Browser log delivery reflects accepted durable entries -Platform SHALL publish browser SSE log events only after a batch is durably accepted. An ingest recovery failure MUST NOT block control heartbeat or lifecycle reporting. - -#### Scenario: A spool segment is quarantined -- **WHEN** Run quarantines an irreconcilable log segment -- **THEN** Platform Web MUST NOT present the rejected body as a live log event -- **AND** the management terminal SHALL continue receiving subsequently accepted log entries diff --git a/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/run-supervised-runtime-observation/spec.md b/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/run-supervised-runtime-observation/spec.md deleted file mode 100644 index 0b419b4..0000000 --- a/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/run-supervised-runtime-observation/spec.md +++ /dev/null @@ -1,39 +0,0 @@ -## ADDED Requirements - -### Requirement: Run reports observed managed-process transitions -Generated Run SHALL report each material observed transition of an autonomous supervised server process through the signed lifecycle report channel. A report MUST identify the bound server and Run endpoint and include the generic process state and bounded exit classification where applicable. - -#### Scenario: Autonomous process starts -- **WHEN** an autonomous generated Run starts its declared supervised process -- **THEN** Run SHALL report `running` after supervision is established -- **AND** Platform SHALL project the server lifecycle state from that Run-owned fact - -#### Scenario: Supervised process exits -- **WHEN** a process supervised by generated Run exits after its initial start report -- **THEN** Run SHALL report the observed `exited` process state and exit classification -- **AND** Platform SHALL project a requested-stop classification as stopped and an unexpected exit as failed - -#### Scenario: Run recovers a managed process journal -- **WHEN** generated Run starts with a persisted managed-process journal -- **THEN** it SHALL check the actual operating-system process state before reporting it -- **AND** it MUST NOT preserve a historical running projection when the recovered process is not alive - -### Requirement: Lifecycle reports are idempotent observed facts -Platform SHALL accept equivalent Run lifecycle observations idempotently and SHALL reject a stale report that would regress a newer observed process transition for the same managed process. - -#### Scenario: Run retries an exit report -- **WHEN** Run retries the same observed exit report after a transport failure -- **THEN** Platform SHALL preserve one equivalent terminal lifecycle projection -- **AND** the retry MUST NOT change the projection back to running - -#### Scenario: Older running observation arrives late -- **WHEN** Platform has accepted a newer terminal observation for a managed process -- **THEN** an older `running` report for that same process MUST NOT overwrite the terminal projection - -### Requirement: Run shutdown does not invent server state -Run shutdown handling SHALL report only process facts that Run has observed through its generic supervisor. Loss of a Run process or heartbeat MUST NOT by itself be reported or projected as a stopped game server. - -#### Scenario: Run becomes unavailable while process state is unknown -- **WHEN** Platform stops receiving heartbeats from a bound Run -- **THEN** Platform MUST retain the last observed lifecycle fact -- **AND** it MUST NOT replace that fact with stopped solely because the endpoint is unavailable diff --git a/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/runtime-observation-freshness/spec.md b/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/runtime-observation-freshness/spec.md deleted file mode 100644 index 46b2c13..0000000 --- a/openspec/changes/repair-run-runtime-state-and-log-recovery/specs/runtime-observation-freshness/spec.md +++ /dev/null @@ -1,35 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform exposes observation freshness with lifecycle projection -Platform SHALL expose the last Run-projected server lifecycle state together with a generic observation freshness derived from the bound Run endpoint's status and heartbeat age. Freshness MUST be distinct from the server lifecycle state. - -#### Scenario: Bound Run is fresh -- **WHEN** the bound Run endpoint has an accepted recent heartbeat within the configured freshness interval -- **THEN** Platform SHALL expose the server runtime observation as fresh -- **AND** the lifecycle projection MAY be presented as currently observed - -#### Scenario: Bound Run heartbeat is overdue -- **WHEN** the bound Run endpoint is offline, disabled, or beyond the configured heartbeat freshness interval -- **THEN** Platform SHALL expose the runtime observation as unverified or unreachable -- **AND** it MUST retain the last lifecycle projection rather than convert it to stopped - -### Requirement: Management views label unverified runtime state -Server list, server detail, and management terminal views SHALL show when a displayed lifecycle state is not currently vouched for by a fresh bound Run. They MUST NOT label a stale lifecycle projection as current process truth. - -#### Scenario: Last observation was running but Run is offline -- **WHEN** the last lifecycle projection is running and the bound Run is unreachable -- **THEN** the management UI SHALL show the last observed running state with an offline or unverified qualifier -- **AND** it MUST NOT display the server as confirmed online solely from the persisted projection - -#### Scenario: Management terminal opens while Run is unverified -- **WHEN** an operator opens the management terminal for a server whose bound Run is unverified -- **THEN** the terminal SHALL show that live delivery depends on Run recovery -- **AND** it SHALL continue to display accepted historical logs through the bounded SSE replay - -### Requirement: Explicit server stop remains Run-observed -An operator-requested stop SHALL remain a Platform-authorized intent executed by Run's generic supervision and plugin-declared lifecycle action. Platform SHALL project stopped only from the resulting Run observation or accepted lifecycle result. - -#### Scenario: Operator stops a server while Run is available -- **WHEN** an authorized operator requests a server stop and the bound Run completes the declared stop action -- **THEN** Platform SHALL project the reported stopped process state -- **AND** the UI SHALL present the result as a current observation while the Run remains fresh diff --git a/openspec/changes/repair-run-runtime-state-and-log-recovery/tasks.md b/openspec/changes/repair-run-runtime-state-and-log-recovery/tasks.md deleted file mode 100644 index bd71c4b..0000000 --- a/openspec/changes/repair-run-runtime-state-and-log-recovery/tasks.md +++ /dev/null @@ -1,41 +0,0 @@ -## 0. Task Boundaries - -正向提示词:为服务器管理第一方区域建立可靠的 Run 运行状态与重启后日志恢复能力。成功标准是:Run 观察到的启动、停止、退出和恢复状态能安全投影到 Platform;Run 重启后管理终端继续收到新日志;Run 离线时界面明确显示状态未验证而不伪造“已停止”。 - -方向提示词:在 `platform/` 实现签名 Run 契约、运行观测投影和日志进度查询,在独立 `git@git.npc0.com:admin343/run.git` 实现通用进程观察与 spool 水位恢复,在 `platform_web/` 展示状态新鲜度。保留插件声明的生命周期和日志源、Platform-Run 通道隔离、现有 SSE 历史回放。验证必须覆盖两个仓库的定向测试、`scripts/check-structure.sh`,以及严格 OpenSpec 校验。 - -任务边界:不得把 `run/` 源码加入本仓库;不得在 Platform 或 Run 写入 SCUM 可执行文件、端口、Steam、路径或文件尾随特例;不得提供浏览器到主机的直接 shell、socket、路径或凭据访问;不得将 Run 失联直接投影为服务器已停止;不得混入账单、云主机销售或第三方平台代理工作流。 - -## 1. Platform Contracts And Projections - -- [x] 1.1 Add typed domain, DTO, API, validation, and protocol contracts for signed Run log-stream progress queries scoped to the authenticated Run endpoint and bound server instance. -- [x] 1.2 Implement Platform log-stream progress lookup that returns only the latest acknowledged sequence and cannot disclose log bodies, host paths, credentials, or another server's stream metadata. -- [x] 1.3 Extend Run lifecycle observations with stable managed-process identity and ordering data, then make lifecycle projection idempotent and reject stale state regressions. -- [x] 1.4 Project autonomous Run recovered and exit observations through the existing signed lifecycle channel, preserving requested-stop versus unexpected-exit classification. -- [ ] 1.5 Expose a server runtime observation view that combines the persisted lifecycle projection with generic bound-endpoint heartbeat freshness without changing lifecycle state solely because Run is unavailable. -- [x] 1.6 Add focused Platform tests for report ordering/idempotency, authorization and scope of log progress, progress values after durable ingest, and fresh versus unverified runtime observation. - -## 2. Independent Run Recovery - -- [x] 2.1 Update the shared/copyable Run-Platform protocol types and API client in `git@git.npc0.com:admin343/run.git` for lifecycle observation ordering and signed log-stream progress reconciliation. -- [x] 2.2 Add atomic per-stream allocated and acknowledged watermark persistence to the Run log spool, including restart loading and acknowledgement-before-segment-deletion ordering. -- [x] 2.3 Replace the worker-global in-memory log counter with stream-specific allocation restored from the spool watermark and pending durable segments. -- [x] 2.4 Reconcile signed Platform stream progress before a stable Run-bound stream with no local watermark emits new entries; cover newly created and recreated-spool cases. -- [x] 2.5 Classify acknowledged-range conflicts and sequence gaps as durable recovery failures, quarantine the affected spool segment with redacted diagnostics, and resume only after safe watermark reconciliation. -- [x] 2.6 Make autonomous process supervision report observed exit and startup-recovery transitions through the lifecycle channel, with retry-safe process identity and ordering metadata. -- [x] 2.7 Define and test graceful Run shutdown behavior that preserves durable state and never reports a server stop unless its generic supervisor observed that process state. -- [x] 2.8 Add Run unit tests for per-stream interleaving, restart continuity, missing-watermark progress lookup, conflict quarantine, process exit reporting, and Windows supervisor recovery. - -## 3. Management Runtime Presentation - -- [x] 3.1 Extend Platform Web API types and server-management contracts to consume lifecycle projection and runtime observation freshness separately. -- [x] 3.2 Update server list and server detail status UI so stale `running` is presented as last observed with a Run offline/unverified qualifier, not confirmed online. -- [x] 3.3 Update the management terminal header and empty/error states to show that live output awaits Run recovery while preserving accepted bounded SSE history. -- [x] 3.4 Add focused frontend tests for fresh, stale, offline, and recovered Run observations plus terminal presentation during log recovery. - -## 4. Cross-Repository Verification And Release - -- [x] 4.1 Run Platform and Run contract compatibility tests for signed progress recovery and lifecycle observation ordering. -- [ ] 4.2 Perform a Windows generated Run scenario covering normal start, supervised process exit, direct Run restart with retained spool, recreated spool reconciliation, quarantined conflict, and operator-requested stop. -- [x] 4.3 Run targeted Go and frontend test suites, `scripts/check-structure.sh`, and `openspec validate repair-run-runtime-state-and-log-recovery --strict`; record the evidence before completing tasks. -- [ ] 4.4 Deploy Platform compatibility before the Run release, then verify runtime freshness and terminal delivery in an environment with no direct browser-to-host access. diff --git a/openspec/changes/repair-server-live-operations-console/.openspec.yaml b/openspec/changes/repair-server-live-operations-console/.openspec.yaml deleted file mode 100644 index e08b5f8..0000000 --- a/openspec/changes/repair-server-live-operations-console/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-03 diff --git a/openspec/changes/repair-server-live-operations-console/design.md b/openspec/changes/repair-server-live-operations-console/design.md deleted file mode 100644 index c236a23..0000000 --- a/openspec/changes/repair-server-live-operations-console/design.md +++ /dev/null @@ -1,57 +0,0 @@ -## Context - -Server cards and server detail headers already consume `GET /api/v1/metrics/server-instances`, but the Platform service currently synthesizes online server values from instance identity and config version when no Run sample exists. The repository already has durable metric sample ingest/query, log stream metadata, cursor-based log queries, job polling, and a SCUM Source RCON dispatch path. The change should connect those existing primitives to the operator UI without adding raw shell access or leaking machine details. - -## Goals / Non-Goals - -**Goals:** -- Make per-server metrics represent the latest real Run sample or a clearly empty/stale state. -- Refresh server list and detail operational data automatically at bounded intervals. -- Keep `编辑部署` on the server list and remove deployment editing from the server detail page. -- Replace the low-value detail overview surface with direct operational sections and drawers for live logs and management terminal. -- Ensure Run job stdout/stderr batches have pre-registered platform log streams and can be accepted using the current Run spool checksum/sequence behavior. -- Keep logs and commands readable in the existing game-operations theme and shared console primitives. - -**Non-Goals:** -- No browser-direct shell, raw socket console, host-path display, credential display, or Run endpoint address display. -- No new run executor source tree, cloud-provider workflow, billing, or marketplace expansion. -- No platform-side game-specific command execution beyond routing through existing plugin/platform-mediated capabilities such as Source RCON. - -## Decisions - -### Decision 1: Latest sample beats synthetic projection - -`ListServerMetricsForSession` will select the most recent persisted `MetricSample` for each visible server. If no sample exists, the response returns only safe identity/online/source/timestamp metadata with no fabricated player, TPS, latency, or resource percentages. The UI will render `--`, `等待上报`, or `指标过期` based on sample freshness. - -Alternative considered: keep deterministic placeholders and label them as simulated. This still makes the server card look operational when it is not, so it is rejected. - -### Decision 2: Polling over new transports - -The frontend will use bounded polling: server cards refresh metrics/jobs every few seconds, detail headers refresh the same projections, and live logs tail with `/log-streams/query` using `afterSeq`. This reuses existing routes and avoids adding WebSocket/SSE infrastructure in the same change. - -Alternative considered: introduce WebSockets for true streaming. That is better long-term, but it would require new transport contracts and channel ownership beyond this targeted repair. - -### Decision 3: Drawers for live operational windows - -Server list actions open drawers/dialogs for `实时日志` and `管理终端`. These windows do not resize cards, cover metrics inside the card, or create a tall action stack. The log drawer supports source selection, pause/resume, manual refresh, clear visible buffer, and autoscroll. The terminal drawer provides an opaque 100%-width, near-full-height command console with command history, bottom input, and quick command templates. It uses the existing SCUM Source RCON dispatch for SCUM servers; unsupported plugins show a safe unsupported state. - -Alternative considered: route every action through the detail page. Operators asked for card-level fast access, so drawers preserve context without forcing navigation. - -### Decision 4: Platform owns job log stream metadata - -Run currently spools process logs using deterministic job stream IDs such as `job..stdout` and uploads single-line batches with a line checksum. Platform job creation will pre-register the matching job stdout/stderr streams, management-program command jobs will also get mediated program stdout/stderr streams, and durable service startup will recover those stream records for legacy jobs persisted before this repair. Log ingest will accept the current Run single-line checksum and first sequence for a new stream while keeping strict continuation once a stream has acknowledged entries. - -Alternative considered: require Run to create streams through a platform-admin route before upload. That would either grant Run broader metadata creation authority or continue to leave normal process logs invisible, so it is rejected for this repair. - -### Decision 5: Detail page starts as an operations workspace - -The detail page no longer exposes deployment editing and does not keep a default overview panel. Its navigation starts on live logs and keeps configuration, plugin controls, AI assistant, operation history, and runtime controls as explicit sections. Runtime binding and distribution controls remain available where they are operational controls, not the create/list deployment editor. - -Alternative considered: keep overview but empty it down. That preserves a tab the user already identified as low-value, so it is removed. - -## Risks / Trade-offs - -- [Risk] Existing local/demo data may have no metric samples and therefore show fewer numbers. → Mitigation: render clear pending/stale labels and retain manual refresh. -- [Risk] Polling can create noisy requests on many cards. → Mitigation: use bounded intervals, reuse existing list endpoints, and clear timers on unmount. -- [Risk] Operators may expect a raw terminal. → Mitigation: label it `管理终端`, show the mediated command target, and make unsupported/direct-shell boundaries explicit in the UI. -- [Risk] CSS changes could drift from the theme. → Mitigation: use shared console/drawer/button primitives and keep CSS declarations compact per repository rules. diff --git a/openspec/changes/repair-server-live-operations-console/proposal.md b/openspec/changes/repair-server-live-operations-console/proposal.md deleted file mode 100644 index 6be3a86..0000000 --- a/openspec/changes/repair-server-live-operations-console/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -Server management currently presents live-looking server values that are not live: per-server players, TPS, latency, CPU, memory, and disk are projected from deterministic platform placeholders when no Run metric sample exists. Operators also need fast log and command access from the server list without losing the safe Platform-mediated boundaries. - -## What Changes - -- Replace placeholder per-server metrics with latest authorized Run metric samples, including clear empty/stale states when no fresh sample exists. -- Add automatic refresh for server list and server detail operational data so cards, task status, and detail headers do not remain frozen until manual refresh. -- Update server cards to show CPU, memory, and disk with visible progress meters and replace the detail/deployment action cluster with direct `实时日志` and `管理终端` entry points while keeping `编辑部署` available only on the server list. -- Remove the low-value server detail overview surface; the detail page becomes an operations workspace focused on logs, management terminal, runtime controls, configuration, plugin controls, AI assistance, and history. -- Introduce a safe live log drawer that tails platform log streams through cursor polling rather than exposing host paths, sockets, or Run credentials. -- Register job log streams and accept the current Run spool checksum/sequence shape so stdout/stderr batches can appear in the live log drawer. -- Introduce a safe management terminal drawer that sends plugin/platform-mediated commands, starting with the existing SCUM Source RCON command path, and shows task submission/status without exposing raw shell access. - -## Capabilities - -### New Capabilities -- `server-live-operations-console`: Real server metric projection, refresh cadence, live log tailing, and safe management terminal behavior for 服务器管理. - -### Modified Capabilities -- None. - -## Impact - -- Affected roots: `platform/` for per-server metric projection and job log stream compatibility, `platform_web/` for server list/detail interaction, polling, logs, terminal UI, and focused tests. -- Existing Run metric ingest, log stream cursor query, job tracking, and SCUM Source RCON command APIs are reused; no new run source tree or browser-direct shell is introduced. -- Verification: focused Go tests for metrics projection, frontend tests/typecheck/build as needed, `scripts/check-structure.sh`, and `openspec validate repair-server-live-operations-console --strict`. diff --git a/openspec/changes/repair-server-live-operations-console/specs/server-live-operations-console/spec.md b/openspec/changes/repair-server-live-operations-console/specs/server-live-operations-console/spec.md deleted file mode 100644 index 9f94705..0000000 --- a/openspec/changes/repair-server-live-operations-console/specs/server-live-operations-console/spec.md +++ /dev/null @@ -1,81 +0,0 @@ -## ADDED Requirements - -### Requirement: Server metrics use real samples -The system SHALL display per-server players, TPS, latency, CPU, memory, and disk from the latest authorized Run metric sample, and SHALL NOT fabricate live-looking values when no sample exists. - -#### Scenario: Fresh metric sample exists -- **WHEN** a visible server has a latest Run metric sample -- **THEN** the server list and detail header show that sample's players, TPS, latency, CPU, memory, and disk values with the sample collection time available to the UI - -#### Scenario: No metric sample exists -- **WHEN** a visible server has no persisted metric sample -- **THEN** the server list and detail header show pending or placeholder states instead of deterministic player, TPS, latency, CPU, memory, or disk values - -#### Scenario: Stale metric sample exists -- **WHEN** a visible server's latest metric sample is older than the frontend freshness threshold -- **THEN** the UI marks the metrics as stale while preserving the last safe values and collection time - -### Requirement: Server operations refresh automatically -The system SHALL refresh server operational projections on a bounded cadence instead of requiring manual refresh for every data update. - -#### Scenario: Server list is open -- **WHEN** an operator is viewing the server list -- **THEN** metrics and task/job status refresh automatically at a bounded interval and stop refreshing when the page unmounts - -#### Scenario: Server detail is open -- **WHEN** an operator is viewing a server detail workspace -- **THEN** the header metrics and job state refresh automatically at a bounded interval and stop refreshing when the page unmounts - -### Requirement: Server list exposes live operation entry points -The server list SHALL keep `编辑部署` on server cards and SHALL provide direct live log and management terminal entry points without moving deployment editing into the detail page. - -#### Scenario: Server card actions render -- **WHEN** an operator views a server card -- **THEN** the card includes `编辑部署`, `实时日志`, and `管理终端` actions using compact controls that do not reflow the card into a command tower - -#### Scenario: Server detail actions render -- **WHEN** an operator views the server detail workspace -- **THEN** the detail header does not render an `编辑部署` action or deployment save workflow - -### Requirement: Live logs tail through platform cursors -The live log window SHALL read platform log streams and entries through authorized platform APIs using bounded cursor polling. - -#### Scenario: Live log drawer opens -- **WHEN** an operator opens `实时日志` for a server -- **THEN** the UI lists authorized log streams for that server and tails selected stream entries using `afterSeq` cursor polling - -#### Scenario: Live log controls are used -- **WHEN** an operator pauses, resumes, clears, filters, or manually refreshes live logs -- **THEN** the UI updates only the visible log window state and does not expose host paths, raw sockets, credentials, or Run endpoint addresses - -#### Scenario: Run job logs are uploaded -- **WHEN** Platform creates a server-scoped job that Run may execute -- **THEN** Platform pre-registers the job stdout and stderr log streams that match Run's deterministic spool stream IDs -- **AND** Run log batches using the current single-line checksum and an initial global sequence number are accepted and visible through cursor queries - -#### Scenario: Legacy persisted jobs have no log streams -- **WHEN** Platform starts with existing server-scoped jobs that were persisted before job log streams were auto-created -- **THEN** Platform recovers the missing job stdout and stderr stream metadata before log cursor recovery -- **AND** mediated management-program jobs recover their program stdout and stderr stream metadata as well - -### Requirement: Management terminal is platform-mediated -The management terminal SHALL dispatch commands only through platform-authorized plugin or lifecycle command paths and SHALL NOT provide browser-direct shell access. - -#### Scenario: SCUM command submitted -- **WHEN** an operator submits a SCUM management command from the terminal -- **THEN** the UI dispatches the command through the existing Source RCON command API and displays safe submission/job status - -#### Scenario: Terminal window opens -- **WHEN** an operator opens `管理终端` -- **THEN** the UI presents an opaque near-full-height command console with bottom command input and quick command templates rather than a translucent narrow drawer - -#### Scenario: Unsupported command target -- **WHEN** a server plugin has no supported management terminal command path -- **THEN** the terminal shows an unsupported state instead of exposing a raw shell or arbitrary command input - -### Requirement: Detail overview is removed -The server detail workspace SHALL omit the low-value overview panel and start from operational sections. - -#### Scenario: Detail navigation renders -- **WHEN** an operator opens server detail -- **THEN** the section navigation excludes `概览` and provides operational sections such as logs, runtime controls, configuration, plugin controls, AI assistant, and operation history diff --git a/openspec/changes/repair-server-live-operations-console/tasks.md b/openspec/changes/repair-server-live-operations-console/tasks.md deleted file mode 100644 index c559ca5..0000000 --- a/openspec/changes/repair-server-live-operations-console/tasks.md +++ /dev/null @@ -1,29 +0,0 @@ -## 1. Backend Metrics Projection - -- [x] 1.1 Update per-server metrics listing to return the latest authorized persisted metric sample instead of deterministic placeholder values. -- [x] 1.2 Add/adjust service tests for latest-sample projection and no-sample placeholder behavior. - -## 2. Frontend Refresh And Metrics Presentation - -- [x] 2.1 Add bounded automatic refresh for server list metrics/jobs and server detail header/job projections. -- [x] 2.2 Update server cards and detail header to show metric freshness, placeholders, and CPU/memory/disk progress meters. - -## 3. Live Logs And Management Terminal - -- [x] 3.1 Add a live log drawer that lists server log streams and tails selected entries with cursor polling plus pause, clear, filter, and manual refresh controls. -- [x] 3.2 Add a management terminal drawer that dispatches SCUM commands through the existing Source RCON API and shows safe unsupported state for other plugins. -- [x] 3.3 Wire server list card actions to `编辑部署`, `实时日志`, and `管理终端` without reflowing cards or moving deployment editing into detail. -- [x] 3.4 Register job stdout/stderr log streams and accept current Run spool checksum/sequence behavior so live logs can populate. -- [x] 3.5 Convert the management terminal to an opaque full-width command console with bottom input and quick command templates. -- [x] 3.6 Recover missing job log streams for legacy persisted jobs at durable service startup. - -## 4. Server Detail Workspace - -- [x] 4.1 Remove the server detail overview section/tab and deployment edit button while keeping operational sections available. -- [x] 4.2 Reuse the live log and management terminal components from detail where appropriate. - -## 5. Verification - -- [x] 5.1 Run focused backend/frontend tests covering metrics, page contracts, and command/log UI. -- [x] 5.2 Run `scripts/check-structure.sh`. -- [x] 5.3 Run `openspec validate repair-server-live-operations-console --strict`. diff --git a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/.openspec.yaml b/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/.openspec.yaml deleted file mode 100644 index ab39675..0000000 --- a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-30 diff --git a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/design.md b/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/design.md deleted file mode 100644 index ec82c24..0000000 --- a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/design.md +++ /dev/null @@ -1,91 +0,0 @@ -## Context - -Platform currently persists `RunEndpoint` rows and stores `ServerInstance.RunEndpointID` as if a server is bound to a durable machine endpoint. The same identifier is then reused for control registration, heartbeat status, job scheduling, UI availability, and component revocation. That model conflicts with the intended architecture: Platform is a registry/dispatcher, while Run is a server-scoped RPC worker that proves possession of a component token, registers a live session, heartbeats, and claims work only while that session lease is current. - -The recent platform-side builder work already removed distribution builds from machine-side Run authority. This change continues that direction by removing durable run endpoint rows from first-party server runtime routing. Redis becomes the required runtime registry for local development, tests, and production so that all environments exercise the same lease and expiry behavior. - -## Goals / Non-Goals - -**Goals:** - -- Replace durable run endpoint routing with Redis-backed runtime session leases. -- Route runtime jobs by logical target (`serverInstanceId`, `componentKind`, optional `componentKey`) instead of endpoint rows. -- Keep server creation, deployment definitions, component auth keys, jobs, logs, artifacts, and audits durable in the database. -- Make runtime online state, capabilities, capacity, session token hashes, and heartbeat freshness ephemeral and TTL-backed. -- Require Redis in development and tests; avoid memory-only behavior hiding lease/routing bugs. -- Preserve platform-owned distribution builds and prevent generated Runs from receiving build authority. - -**Non-Goals:** - -- Do not introduce cloud hosting, SSH provisioning, billing, or external marketplace workflows. -- Do not store raw credentials, host paths, sockets, or plaintext component keys in Redis. -- Do not move durable job history, audit events, artifacts, or server definitions out of the database. -- Do not re-add a `run/` source tree to this repository. - -## Decisions - -### 1. Redis is the only runtime registry for dev, test, and production - -Platform SHALL require Redis for runtime registry behavior in every normal environment. Tests may start an isolated Redis instance or use an explicit test Redis database/prefix, but they SHALL NOT swap in a memory registry for ordinary execution. - -This keeps expiration, reconnect, session loss, and multi-process behavior visible during development. An in-memory registry would be simpler, but it would let tests pass with semantics that fail once Platform runs more than one process or restarts. - -### 2. Registry data is ephemeral and TTL-owned - -Redis stores only live-session data: - -- `runtime:v1:session:{serverInstanceId}:{componentKind}:{componentKey}` -- `runtime:v1:token:{tokenHash}` -- optional short-lived indexes for online summaries and capability snapshots - -Every key has a TTL derived from the heartbeat interval plus a small grace window. Platform startup does not scan or clean Redis. Stale sessions expire naturally, and a Redis flush/restart is treated like all Runs temporarily went offline until they register again. - -### 3. Component token identity replaces endpoint ownership - -A generated Run hello includes `serverInstanceId`, `componentKind=run`, component key/generation, registration proof, version, target OS/arch, capabilities, and capacity. Platform authenticates the component key from durable database state, then writes a Redis session lease. The accepted session token is scoped to that server/component and is used for heartbeat, claim, ack, progress, result, logs, and artifact operations. - -The system does not need a pre-existing endpoint row or a server-to-endpoint foreign key. If two Runs present the same component identity, the later valid registration supersedes the previous session by rotating the token lease. - -### 4. Jobs target logical components, not endpoints - -Durable jobs use a logical target: - -- `serverInstanceId` -- `targetComponentKind` such as `run`, `client-manager`, or `platform-builder` -- `targetComponentKey` for keyed components, empty for the server Run - -Run job claim authenticates the session token, derives the server/component target from Redis, and returns only eligible jobs for that target. Leases remain durable on the job record so retries and audit history survive Platform restarts. - -### 5. UI shows runtime connection health, not endpoint selection - -Platform Web replaces endpoint selection/status surfaces with server runtime connection health. The user sees whether the generated Run is registered, heartbeat freshness, version, target OS/arch, capabilities, current capacity, and safe unavailable reasons. The UI does not ask the owner to choose a run endpoint when creating, editing, or deploying a server. - -### 6. Compatibility is staged but not permanent - -Existing code paths that accept `runEndpointId` become compatibility shims during migration. They should either translate to logical targets where safe or return a clear deprecation validation error in first-party workflows. New code must not create `RunEndpoint` rows for machine-side generated Runs. - -## Risks / Trade-offs - -- [Redis unavailable blocks runtime routing] -> Treat Redis as required platform infrastructure; startup/health checks must report runtime registry unavailable and runtime operations must fail safely without dispatching jobs. -- [Redis restart marks healthy Runs offline until reconnect] -> Runs already heartbeat frequently; clients retry registration when heartbeat fails or receives an unknown-session response. -- [Large migration surface] -> Move in phases: registry interface first, job target fields second, UI/API cleanup third, repository deletion last. -- [Old endpoint-based packages reconnect] -> Compatibility can accept legacy registration only behind explicit migration rules; generated packages should be rebuilt with server/component identity. -- [Tests become slower with Redis] -> Use a test Redis prefix/database and cleanup by prefix in test setup, while still relying on TTL behavior for session expiry scenarios. - -## Migration Plan - -1. Add Redis configuration, health checks, and test harness support; fail fast when Redis is unavailable. -2. Introduce `RuntimeSessionRegistry` backed by Redis and move Run hello/heartbeat/token validation onto it. -3. Add durable job logical target fields while temporarily writing both logical target and legacy `runEndpointId`. -4. Change job claim, ack, progress, result, log, artifact, config, file, and lifecycle dispatch to authorize through registry-derived server/component targets. -5. Remove first-party server creation/edit/deployment references to run endpoint selection and project runtime connection health from Redis. -6. Migrate or deprecate legacy endpoint-based records; stop creating `RunEndpoint` rows for generated Runs. -7. Remove obsolete repository methods, DTO fields, tests, and documentation once compatibility paths are no longer used. - -Rollback is limited to keeping the compatibility shim and disabling new runtime dispatch. Durable server definitions and jobs remain in the database; Redis contains only disposable session leases. - -## Open Questions - -- Should Redis be required at Platform process startup, or can only runtime routes fail health checks while non-runtime admin pages stay available? -- Which Redis deployment profile should local scripts use by default: Docker Compose service, existing local Redis, or a repo-managed test container? -- How long should the legacy `runEndpointId` compatibility window remain before API fields are removed? diff --git a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/proposal.md b/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/proposal.md deleted file mode 100644 index 55a0bb9..0000000 --- a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -The platform currently treats `RunEndpoint` as both a durable database resource and a live runtime connection. That creates the wrong product model: Platform should behave like a registry and dispatcher, while Run should behave like an authenticated RPC worker that registers, heartbeats, and receives work only while its token-backed session is alive. - -## What Changes - -- **BREAKING** Remove first-party server-to-`RunEndpoint` persistence as the runtime routing model; server records no longer bind to a durable run endpoint row. -- Introduce a Redis-backed runtime session registry for Run registration, heartbeat leases, capability snapshots, capacity snapshots, and token/session lookup. -- Require development, test, and production flows to use Redis for runtime registry behavior; do not add an in-memory registry fallback for normal test execution. -- Retarget durable jobs from endpoint rows to logical server/component targets such as `serverInstanceId + componentKind + componentKey`. -- Keep platform-owned distribution builds separate from machine-side Run sessions; build routing remains a platform builder responsibility, not a registered Run endpoint capability. -- Replace user-facing run endpoint selection/status with runtime connection health derived from Redis leases and durable component/server records. - -## Capabilities - -### New Capabilities - -- `redis-runtime-session-registry`: Redis-backed registration, heartbeat, token validation, routing, and capability snapshots for machine-side Run sessions. - -### Modified Capabilities - -- `platform-side-distribution-builds`: Remove requirements that bind a server instance to a generated run endpoint; preserve platform-owned builds while using runtime registration rather than durable endpoint rows. - -## Impact - -- `platform/`: domain types, DTOs, repositories, service scheduling, Run control registration, job claim/lease validation, server lifecycle dispatch, runtime action availability, tests, and local dev/test setup. -- `platform_web/`: server management API types, runtime connection status UI, run generation/deployment copy, and tests that currently reference run endpoints. -- `plugins/`: plugin bridge and companion-facing contracts where they expose or consume run endpoint identifiers. -- Infrastructure: Redis becomes a required dependency for dev, test, and production runtime registry behavior. diff --git a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/specs/platform-side-distribution-builds/spec.md b/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/specs/platform-side-distribution-builds/spec.md deleted file mode 100644 index 59bee12..0000000 --- a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/specs/platform-side-distribution-builds/spec.md +++ /dev/null @@ -1,42 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Server creation requires only plugin type and server name -The system SHALL require only the game plugin type and the server name to create a server instance, and SHALL NOT require a deployment target, run endpoint, or runtime profile at creation time. The creation workflow MAY collect plugin-declared deployment mode, game configuration, and startup fields before submit, but those fields SHALL NOT create a durable run endpoint binding. - -#### Scenario: Creation form field set -- **WHEN** an owner opens the server creation workflow -- **THEN** the form requires plugin type and server name only, may present plugin-declared deployment/startup inputs, and presents no deployment target or run endpoint selector as a creation prerequisite - -#### Scenario: Creation without any registered runtime session -- **WHEN** an owner creates a server instance while no Run has registered for that instance -- **THEN** creation succeeds and the instance is created without a run endpoint binding - -#### Scenario: Runtime session established by run registration -- **WHEN** a generated Run for that instance registers itself with the platform -- **THEN** the platform creates or renews a Redis runtime session lease for that server Run and does not persist a server-to-run-endpoint association - -#### Scenario: Runtime profile and endpoint selection are not creation prerequisites -- **WHEN** an owner opens an already-created instance -- **THEN** runtime profile and run endpoint selection are not required to make the instance exist, generate a Run package, or show runtime connection guidance - -### Requirement: Build availability is independent of run endpoint capabilities -The system SHALL determine `generate-run` and `generate-client-manager` availability from plugin declarations, runtime bindings, and platform builder readiness, and SHALL NOT require any machine-side Run session or durable run endpoint row to advertise `distribution.build`. - -#### Scenario: Instance has only its generated Run runtime session -- **WHEN** a server instance's only live runtime session is its generated Run, which holds no distribution-build authority -- **THEN** `generate-run` remains available and a new run distribution can be generated through the platform builder - -#### Scenario: No privileged worker endpoint registered -- **WHEN** no machine-side Run advertises `distribution.build` or no legacy endpoint row exists -- **THEN** run generation still succeeds through the platform Docker builder - -#### Scenario: Builder unavailable -- **WHEN** the platform Docker builder is unavailable -- **THEN** the unavailable reason names the platform builder rather than a run endpoint capability - -### Requirement: Generated runs hold no distribution-build authority -The system SHALL continue to deny distribution-build work to component-authenticated generated Runs. This restriction is a security boundary and SHALL NOT be relaxed to unblock building. - -#### Scenario: Generated run claims a build -- **WHEN** a component-authenticated generated Run session claims work advertising `distribution.build` -- **THEN** the platform does not assign distribution build work to that Run session diff --git a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/specs/redis-runtime-session-registry/spec.md b/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/specs/redis-runtime-session-registry/spec.md deleted file mode 100644 index 0101e6b..0000000 --- a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/specs/redis-runtime-session-registry/spec.md +++ /dev/null @@ -1,72 +0,0 @@ -## ADDED Requirements - -### Requirement: Redis-backed runtime session registry is required -The system SHALL use Redis as the runtime session registry in development, test, and production environments, and SHALL NOT use an in-memory registry fallback for normal runtime registration, heartbeat, token validation, or job routing. - -#### Scenario: Platform starts without Redis -- **WHEN** Platform starts or checks health while Redis is unavailable -- **THEN** runtime registry health is reported unavailable and runtime dispatch operations fail safely without assigning jobs to Runs - -#### Scenario: Automated tests exercise Redis registry behavior -- **WHEN** tests cover Run registration, heartbeat expiry, token validation, or job claim routing -- **THEN** those tests use an isolated Redis database or key prefix rather than a memory-only registry - -### Requirement: Run registration creates an ephemeral Redis session -The system SHALL authenticate generated Runs with durable component credentials and SHALL store only a short-lived Redis session lease for the live Run connection. - -#### Scenario: Valid generated Run registers -- **WHEN** a Run submits a hello request with a valid server instance ID, component kind, component key generation, registration proof, version, target, capabilities, and capacity -- **THEN** Platform authenticates the durable component key and writes a Redis session lease scoped to that server/component identity - -#### Scenario: Registration supersedes prior live session -- **WHEN** a second valid Run registers for the same server/component identity -- **THEN** Platform rotates the live Redis session token and the previous session token no longer authorizes heartbeat or job operations - -#### Scenario: Invalid token is rejected -- **WHEN** a Run presents an invalid registration proof or stale component key generation -- **THEN** Platform rejects registration and does not create or renew a Redis session lease - -### Requirement: Heartbeat leases expire without startup cleanup -The system SHALL represent runtime online state through Redis TTL leases that are renewed by heartbeats and naturally expire without Platform startup cleanup. - -#### Scenario: Heartbeat renews lease -- **WHEN** a registered Run heartbeats with the current session token before the Redis TTL expires -- **THEN** Platform renews the Redis lease and updates the safe capability/capacity snapshot - -#### Scenario: Run stops heartbeating -- **WHEN** a Run stops heartbeating beyond the configured expiry window -- **THEN** Redis expires the session keys and Platform reports that runtime connection as offline - -#### Scenario: Platform restarts -- **WHEN** Platform restarts while Redis still contains live session keys -- **THEN** Platform resumes token validation and routing from Redis without scanning or cleaning stale keys at startup - -#### Scenario: Redis loses session data -- **WHEN** Redis restarts or evicts runtime session keys -- **THEN** Platform treats affected Runs as offline until they register again and does not mutate durable server or job records solely because the Redis lease disappeared - -### Requirement: Runtime jobs target server components instead of run endpoints -The system SHALL route durable runtime jobs by logical server/component target and SHALL NOT require a durable run endpoint row to create, validate, claim, or complete machine-side runtime work. - -#### Scenario: Job is queued for a server Run -- **WHEN** Platform queues lifecycle, config, file, log, or protected-request work for a server Run -- **THEN** the durable job target identifies the server instance and `run` component rather than a run endpoint ID - -#### Scenario: Run claims work -- **WHEN** a registered Run claims work with its current session token -- **THEN** Platform derives the server/component target from Redis and assigns only eligible jobs for that target - -#### Scenario: Run attempts cross-server claim -- **WHEN** a Run session for one server attempts to claim, acknowledge, report progress, or complete a job targeting another server/component -- **THEN** Platform rejects the operation and preserves the durable job state - -### Requirement: Runtime connection projections are safe -The system SHALL expose runtime connection health as a safe projection derived from Redis leases and durable server/component metadata, without exposing session tokens, Redis keys, raw credentials, host paths, or direct sockets. - -#### Scenario: Owner views server runtime health -- **WHEN** an authorized owner views a server's runtime connection state -- **THEN** Platform returns safe status, last heartbeat time, version, target OS/architecture, capabilities, capacity, and unavailable reason - -#### Scenario: Runtime session secrets remain hidden -- **WHEN** Platform Web, plugin pages, or bridge actions request runtime status -- **THEN** responses exclude session tokens, token hashes, Redis key names, raw component credentials, host paths, and sockets diff --git a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/tasks.md b/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/tasks.md deleted file mode 100644 index a1836fd..0000000 --- a/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/tasks.md +++ /dev/null @@ -1,40 +0,0 @@ -## 1. Prompt Boundaries - -- [ ] 1.1 Positive prompt: replace durable run endpoint routing with a Redis-backed runtime registry so the first-party server management area can route work to authenticated Run sessions without requiring endpoint selection or server-to-endpoint database binding. -- [ ] 1.2 Directional prompt: work inside `platform/`, `platform_web/`, `plugins/`, OpenSpec contracts, and local dev/test scripts; preserve platform-side distribution builds, component-token authentication, channel isolation, and existing magical-girl console styling. -- [ ] 1.3 Boundary prompt: do not add cloud host sales, billing, SaaS marketplace features, a `run/` source tree, host-path exposure, raw credentials, direct sockets, or any fallback that dispatches runtime work without a Redis-backed session lease. - -## 2. Redis Registry Foundation - -- [ ] 2.1 Add Redis configuration and health reporting for development, test, and production runtime registry use. -- [ ] 2.2 Add local/test Redis setup so automated tests use isolated Redis keys or databases rather than memory-only runtime registry behavior. -- [ ] 2.3 Define `RuntimeSessionRegistry` with Redis-backed register, heartbeat, lookup-by-token, lookup-by-server-component, revoke, and projection methods. -- [ ] 2.4 Implement Redis key namespaces, TTL renewal, token hashing, capability/capacity snapshots, and no-startup-cleanup semantics. - -## 3. Run Registration And Session Auth - -- [ ] 3.1 Change Run hello to authenticate server/component identity and write a Redis session lease instead of creating or updating a durable `RunEndpoint`. -- [ ] 3.2 Change heartbeat to renew Redis leases and return unknown-session responses that cause Run to re-register. -- [ ] 3.3 Move run request signature and session-token validation to registry-derived sessions while preserving nonce and clock-skew protection. -- [ ] 3.4 Update revocation and component key reset to revoke Redis sessions for the affected server/component without relying on endpoint rows. - -## 4. Job Targeting And Dispatch - -- [ ] 4.1 Add durable job target fields for `serverInstanceId`, `targetComponentKind`, and `targetComponentKey`, with compatibility for existing `runEndpointId` data during migration. -- [ ] 4.2 Update job creation and idempotency to target logical components rather than run endpoints, keeping platform builder jobs as a platform-owned target. -- [ ] 4.3 Update claim, ack, progress, result, cancel, reconcile, log ingest, artifacts, config writes, file operations, protected requests, and lifecycle dispatch to authorize through Redis session targets. -- [ ] 4.4 Remove validation that requires server jobs to match `ServerInstance.RunEndpointID`, replacing it with server/component target validation and Redis session presence where dispatch requires a live Run. - -## 5. API, UI, And Compatibility Cleanup - -- [ ] 5.1 Remove first-party creation/edit/deploy flows that ask for run endpoint selection or persist server-to-run-endpoint bindings. -- [ ] 5.2 Replace run endpoint list/status UI with runtime connection health projections derived from Redis sessions and durable server/component metadata. -- [ ] 5.3 Update API DTOs, docs, plugin bridge contracts, and tests to mark `runEndpointId` as legacy compatibility where still accepted. -- [ ] 5.4 Remove durable `RunEndpoint` repository usage for generated Runs after compatibility tests cover legacy records. - -## 6. Verification - -- [ ] 6.1 Add backend tests for Redis registration, TTL expiry, token rotation, Redis restart/loss, cross-server claim rejection, and job lease behavior. -- [ ] 6.2 Add frontend tests for runtime connection health and absence of run endpoint selectors in first-party server workflows. -- [ ] 6.3 Run `go test ./...`, `npm --prefix platform_web test -- --run`, `scripts/check-structure.sh`, and targeted Redis integration tests. -- [ ] 6.4 Run `openspec validate replace-run-endpoints-with-redis-runtime-registry --strict` before marking implementation tasks complete. diff --git a/openspec/changes/report-run-server-metrics/.openspec.yaml b/openspec/changes/report-run-server-metrics/.openspec.yaml deleted file mode 100644 index d7bc011..0000000 --- a/openspec/changes/report-run-server-metrics/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-08-10 diff --git a/openspec/changes/report-run-server-metrics/design.md b/openspec/changes/report-run-server-metrics/design.md deleted file mode 100644 index 8e0f1d9..0000000 --- a/openspec/changes/report-run-server-metrics/design.md +++ /dev/null @@ -1,38 +0,0 @@ -## Context - -Platform already stores signed metric batches and explicitly marks a server as `run-metrics-pending` only until its generated Run reports a sample. Run has the protocol client method but no collector or caller, even when an autonomous lifecycle has started a managed process. - -## Goals / Non-Goals - -**Goals:** - -- Report an immediate sample after the autonomous lifecycle and a refreshed sample after accepted heartbeats. -- Use only supervised-process state for online status and generic local resource measurements for utilization fields. -- Keep request timeouts bounded and failures non-fatal. - -**Non-Goals:** - -- Infer player count, max players, TPS, latency, or any SCUM-specific semantics. -- Make platform lifecycle projections authoritative for process state. -- Add a metric spool, alter the platform metrics route, or expose local filesystem paths. - -## Decisions - -- The worker owns reporting because it owns the active registration session and calls the existing signed metric endpoint. A sample is sent once registration, reconciliation, and autonomous startup have completed, then after each successful heartbeat. -- Online is `true` only if a persisted managed process matching the generated Run's endpoint and server is still `running` according to its supervisor. If no matching process exists or it has exited, Run reports `online=false`; it never derives this fact from the platform's stale projection. -- A platform-independent collector reads generic host CPU, memory, and the filesystem containing the Run workspace. Collector failures omit the affected optional field rather than failing the sample. The values use percentages in the existing protocol fields. -- Each request uses a short timeout. Errors are logged as degraded reporting and the main worker continues; the next heartbeat retries with a fresh sample. This preserves the control/jobs/logs/artifacts channel boundaries. - -## Risks / Trade-offs - -- [Host APIs vary by OS] → isolate collection behind a runtime collector and test it with a deterministic fake; unavailable fields remain omitted. -- [Run restart loses a prior sample] → report immediately after autonomous bootstrap and on every heartbeat; platform already retains accepted observations. -- [A stale process record] → ask the supervisor for current status before deriving online state. - -## Migration Plan - -No persistence migration is needed. Existing deployed Runs continue showing pending until replaced; new Runs report on their first successful registration/startup cycle. Rolling back simply stops new samples while existing platform history remains valid. - -## Open Questions - -- None. diff --git a/openspec/changes/report-run-server-metrics/proposal.md b/openspec/changes/report-run-server-metrics/proposal.md deleted file mode 100644 index 34ff42d..0000000 --- a/openspec/changes/report-run-server-metrics/proposal.md +++ /dev/null @@ -1,25 +0,0 @@ -## Why - -Generated Run packages register and supervise their server process, but never submit a metric sample. The platform consequently has no observed metrics and correctly renders every such server as waiting for Run reporting. - -## What Changes - -- Add Run-side periodic metric reporting for its bound server instance after registration. -- Derive online state only from Run's generic supervised-process records; report no game-specific player, TPS, or latency values. -- Include bounded host CPU, memory, and workspace-volume usage when the operating system collector can observe them, without exposing paths or credentials. -- Keep metric delivery on an independent, bounded reporting path so failed metric uploads do not block control, lifecycle jobs, logs, or artifacts. - -## Capabilities - -### New Capabilities - -- `run-server-metrics`: Generic server-process and host-resource metric collection and Run-to-platform reporting. - -### Modified Capabilities - -- None. - -## Impact - -- Affects the independent `run/` runtime worker, its platform client contract implementation, and Run tests. -- Uses the existing signed `POST /api/v1/run/metrics/batches` platform route; no browser or game-plugin contract changes are required. diff --git a/openspec/changes/report-run-server-metrics/specs/run-server-metrics/spec.md b/openspec/changes/report-run-server-metrics/specs/run-server-metrics/spec.md deleted file mode 100644 index b0221ed..0000000 --- a/openspec/changes/report-run-server-metrics/specs/run-server-metrics/spec.md +++ /dev/null @@ -1,38 +0,0 @@ -## ADDED Requirements - -### Requirement: Run reports supervised server observations - -A generated Run package SHALL submit a signed metric sample for its bound server instance after it has registered and completed autonomous lifecycle startup, and after each successful heartbeat. The sample's online field SHALL be derived from the current state of Run's matching supervised process, not from a platform lifecycle projection. - -#### Scenario: Managed server process is running - -- **WHEN** the generated Run has a current matching supervised process in the `running` state -- **THEN** it submits a metric sample with `online=true` for its bound server instance - -#### Scenario: Managed server process is absent or exited - -- **WHEN** the generated Run has no matching supervised process or its matching process is not running -- **THEN** it submits a metric sample with `online=false` and does not claim the server is running - -### Requirement: Run reports only generic local utilization - -Run SHALL populate optional utilization fields only from generic local host and workspace-volume observations. It SHALL omit unavailable observations and SHALL NOT synthesize game-specific player, TPS, or latency metrics. - -#### Scenario: Host resource collector is available - -- **WHEN** CPU, memory, or workspace-volume utilization can be observed locally -- **THEN** the corresponding percentage fields are included in the sample without any host path or credential value - -#### Scenario: Host resource collector is unavailable - -- **WHEN** an optional local resource observation cannot be collected -- **THEN** Run still reports the process observation and omits the unavailable field - -### Requirement: Metric delivery remains bounded and independent - -Run SHALL use a bounded report request and SHALL treat a rejected or failed metric upload as degraded telemetry rather than a control, job, log, or artifact failure. - -#### Scenario: Metric endpoint is temporarily unavailable - -- **WHEN** an upload attempt fails or times out -- **THEN** the worker continues its lifecycle and retry occurs through a later reporting cycle diff --git a/openspec/changes/report-run-server-metrics/tasks.md b/openspec/changes/report-run-server-metrics/tasks.md deleted file mode 100644 index 5cab51b..0000000 --- a/openspec/changes/report-run-server-metrics/tasks.md +++ /dev/null @@ -1,11 +0,0 @@ -## 1. Run metric reporting - -- [x] 1.1 Extend the Run worker's client boundary and reporting cycle for signed metric batch ingestion with its current registration session. -- [x] 1.2 Add generic supervised-process online-state derivation and optional local host/workspace utilization collection. -- [x] 1.3 Invoke bounded reporting after autonomous startup and successful heartbeats without coupling failure to control, job, log, or artifact loops. - -## 2. Verification - -- [x] 2.1 Add deterministic runtime coverage for session-scoped requests, running/exited process state, optional collector failures, and degraded upload behavior. -- [x] 2.2 Run `go test ./...` in `run/`, `scripts/check-structure.sh`, and `openspec validate report-run-server-metrics --strict`. -- [x] 2.3 Build the Windows Run package, deploy it to the configured Qinghuo server, and confirm the platform receives a non-pending metric observation. diff --git a/openspec/changes/restrict-server-delete-with-password-confirmation/.openspec.yaml b/openspec/changes/restrict-server-delete-with-password-confirmation/.openspec.yaml deleted file mode 100644 index c0a8162..0000000 --- a/openspec/changes/restrict-server-delete-with-password-confirmation/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-21 diff --git a/openspec/changes/restrict-server-delete-with-password-confirmation/design.md b/openspec/changes/restrict-server-delete-with-password-confirmation/design.md deleted file mode 100644 index 0e149a4..0000000 --- a/openspec/changes/restrict-server-delete-with-password-confirmation/design.md +++ /dev/null @@ -1,35 +0,0 @@ -## Context - -Server deletion currently reuses the archive path and already restricts the action to the instance owner or a platform administrator. What it does not do is re-check the caller's password before removing the server from active use, which leaves a destructive action one click away once a session is active. - -## Goals / Non-Goals - -**Goals:** -- Require a password confirmation before server deletion is accepted. -- Preserve the existing owner/platform-admin authorization rule. -- Keep the current soft-delete behavior that marks the server instance deleted and preserves history. - -**Non-Goals:** -- Implementing hard delete or permanent record erasure. -- Changing unrelated server lifecycle permissions. -- Adding a new authentication system or password reset flow. - -## Decisions - -- Keep the existing `DELETE /api/v1/server-instances/{id}` route and extend it with a JSON body containing the current password. This avoids inventing a parallel delete endpoint and keeps the UI and API aligned. -- Verify deletion authorization in the service layer, not only in the frontend. The request must still be rejected even if the browser skips the confirmation UI. -- Reuse the current session user's stored password hash and existing `verifyPassword` helper. No new credential store or token exchange is needed. -- Return a generic forbidden response when the password confirmation fails. The UI can present that as a password-confirmation failure without exposing hash or account details. -- Surface deletion from the server list card's "运行操作" popover in a "危险操作" group instead of placing it inside the detail metadata panel. Runtime actions remain permission-gated, while eligible creators/owners and platform admins can still reach the delete confirmation. -- Update the user-facing copy from "归档" to "删除" so the destructive intent is clear wherever the action is exposed. - -Alternatives considered: -- Separate confirm endpoint: rejected because it adds another round trip without changing the security model. -- Query-string password: rejected because sensitive data should not live in the URL. -- Hard delete: rejected because the platform already models server removal as a deleted state with retained history. - -## Risks / Trade-offs - -- [Risk] Sending a password in the request body increases sensitivity of the delete call. → The request already runs over authenticated HTTPS; the frontend must avoid persisting the value beyond the dialog. -- [Risk] The UI and API could drift if one side keeps "archive" wording or if the delete entry reappears in details. → Keep the confirmation dialog and API call site in the list runtime action flow together. -- [Risk] Password confirmation may feel redundant to power users. → Keep the rule limited to destructive deletion only, not to normal lifecycle operations. diff --git a/openspec/changes/restrict-server-delete-with-password-confirmation/proposal.md b/openspec/changes/restrict-server-delete-with-password-confirmation/proposal.md deleted file mode 100644 index c141638..0000000 --- a/openspec/changes/restrict-server-delete-with-password-confirmation/proposal.md +++ /dev/null @@ -1,23 +0,0 @@ -## Why - -Server deletion currently trusts role and ownership alone, which is too loose for a destructive action. The UI also lets users trigger deletion without re-entering their password, so a stolen session or stray click can remove a server too easily. - -## What Changes - -- Require server delete to be explicitly confirmed with the current user password. -- Allow deletion only for the server creator/owner or a platform administrator. -- Keep the existing archive/delete flow, but expose the destructive action from the server list runtime actions with an intentional password confirmation. -- Return a clear authorization or password error when the confirmation fails. - -## Capabilities - -### New Capabilities -- `server-deletion`: deletion authorization and password confirmation for server instances. - -### Modified Capabilities - -## Impact - -- `platform/` delete handler, service authorization, and password verification logic. -- `platform_web/` server list runtime-action delete confirmation dialog and API client request payload. -- Automated tests covering authorization, password failure, and successful deletion. diff --git a/openspec/changes/restrict-server-delete-with-password-confirmation/specs/server-deletion/spec.md b/openspec/changes/restrict-server-delete-with-password-confirmation/specs/server-deletion/spec.md deleted file mode 100644 index 4b9e8e0..0000000 --- a/openspec/changes/restrict-server-delete-with-password-confirmation/specs/server-deletion/spec.md +++ /dev/null @@ -1,34 +0,0 @@ -## ADDED Requirements - -### Requirement: Authorized server deletion -The system SHALL allow a server instance to be deleted only when the authenticated user is the instance owner or a platform administrator. - -#### Scenario: Owner deletes a server -- **WHEN** the instance owner submits a delete request for their server -- **THEN** the system SHALL accept the request if all other delete checks pass - -#### Scenario: Non-owner cannot delete -- **WHEN** an authenticated user who is neither the owner nor a platform administrator submits a delete request -- **THEN** the system SHALL reject the request with forbidden access - -### Requirement: Password confirmation for deletion -The system SHALL require the authenticated user to provide their current account password with every server delete request and SHALL reject the request if the password is missing or does not match the current session user. - -#### Scenario: Password mismatch -- **WHEN** the authenticated user submits the delete request with an incorrect password -- **THEN** the system SHALL reject the request with forbidden access - -#### Scenario: Password required -- **WHEN** the authenticated user submits the delete request without a password -- **THEN** the system SHALL reject the request as invalid input or forbidden access - -### Requirement: Safe server removal state -The system SHALL continue to reject deletion when the server instance is running or installing, and SHALL otherwise mark the server instance as deleted while preserving historical records. - -#### Scenario: Running server cannot be deleted -- **WHEN** a delete request targets a running server instance -- **THEN** the system SHALL reject the request and keep the server instance intact - -#### Scenario: Successful deletion marks deleted state -- **WHEN** a valid delete request targets a stopped or ready server instance -- **THEN** the system SHALL mark the server instance as deleted and return the updated instance diff --git a/openspec/changes/restrict-server-delete-with-password-confirmation/tasks.md b/openspec/changes/restrict-server-delete-with-password-confirmation/tasks.md deleted file mode 100644 index d38044e..0000000 --- a/openspec/changes/restrict-server-delete-with-password-confirmation/tasks.md +++ /dev/null @@ -1,20 +0,0 @@ -## 1. Backend delete confirmation - -- [x] 1.1 Add a server delete request DTO and extend the service/API contract to accept the current session password on delete. -- [x] 1.2 Verify the current session password in the server deletion flow after owner/admin authorization and keep the existing deleted-state behavior. -- [x] 1.3 Update API handler docs and backend tests for owner/admin success, password failure, and unsafe-state rejection. - -## 2. Frontend delete flow - -- [x] 2.1 Update the server detail delete confirmation dialog to collect a password and submit it with the delete request. -- [x] 2.2 Rename the user-facing action copy from archive to delete where the destructive action is exposed. -- [x] 2.3 Update the API client, contracts, and frontend tests for the new delete payload and confirmation state. - -## 3. Verification - -- [x] 3.1 Run the structure check and focused backend/frontend tests for the delete flow. - -## 4. Follow-up UI placement - -- [x] 4.1 Move the delete confirmation entry from server detail metadata to the server list runtime action popover. -- [x] 4.2 Update frontend tests and verification for the new delete entry placement. diff --git a/openspec/changes/reveal-server-deployment-inputs/.openspec.yaml b/openspec/changes/reveal-server-deployment-inputs/.openspec.yaml deleted file mode 100644 index 2bc06e0..0000000 --- a/openspec/changes/reveal-server-deployment-inputs/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-26 diff --git a/openspec/changes/reveal-server-deployment-inputs/design.md b/openspec/changes/reveal-server-deployment-inputs/design.md deleted file mode 100644 index c04cb36..0000000 --- a/openspec/changes/reveal-server-deployment-inputs/design.md +++ /dev/null @@ -1,40 +0,0 @@ -## Context - -Platform persists each server's deployment definition, including host paths and lifecycle commands. The normal deployment endpoint intentionally exposes only configured flags, so the editor starts these fields blank. This protects operational data in ordinary reads but prevents an authorized operator from comparing the saved definition with Run behavior. - -## Goals / Non-Goals - -**Goals:** - -- Let an authorized server manager explicitly retrieve and inspect that server's saved deployment inputs. -- Make the editor visibly require this explicit action before it receives raw values. -- Preserve redaction on all existing read projections. -- Expose a safe indication of the latest lifecycle job's deployed revision and whether its leased input contained a deployment definition. - -**Non-Goals:** - -- No host browsing, direct Run connection, log changes, secret exposure, or changes to the Run protocol. -- No display of runtime-binding credentials. -- No attempt to infer what a previous Run process actually executed from Platform alone. - -## Decisions - -1. Add a separate authenticated `GET /server-instances/{id}/deployment/reveal` endpoint rather than expanding the existing deployment read response. This makes the security-sensitive action explicit and preserves compatibility for all existing consumers. -2. Reuse server-owner authorization and return only the persisted path/command fields plus non-sensitive deployment metadata. Runtime bindings are excluded because they can contain credential references or secrets. -3. Add a reveal button to the edit workflow. It calls the endpoint only after operator intent, replaces the local blank fields, and provides a clear success/error state. A plain close/reopen returns to the normal redacted form. -4. Derive dispatch diagnostics from the current server's lifecycle jobs. It reports revision and deployment-input presence, not the leased fields themselves. Platform can prove a job was queued with the stored revision; only Run's own safe job result can prove successful execution. - -## Risks / Trade-offs - -- [An authorized browser session can now read operational paths/commands] → The sensitive response requires explicit owner-authorized access, is not cached in existing list/detail endpoints, and the UI fetches it only after an intentional action. -- [Operators could confuse queued dispatch with successful execution] → Label the diagnostic as dispatch evidence and separately retain Run job state/progress. -- [Sensitive values could linger in a browser tab] → Keep them only in the open workflow state and clear them when the dialog closes. - -## Migration Plan - -1. Deploy the additive endpoint and frontend reveal action. -2. Roll back by removing the reveal route/UI; existing saved deployment definitions and normal redacted reads remain unchanged. - -## Open Questions - -- None. The requested scope is explicit authorized display of existing deployment values. diff --git a/openspec/changes/reveal-server-deployment-inputs/proposal.md b/openspec/changes/reveal-server-deployment-inputs/proposal.md deleted file mode 100644 index 6768384..0000000 --- a/openspec/changes/reveal-server-deployment-inputs/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -Operators cannot inspect the exact protected deployment paths and commands that Platform will send to Run. The current configured-only editor makes it impossible to verify an existing deployment or investigate whether Run used the intended inputs. - -## What Changes - -- Add an authorized, explicit read path for a server manager to reveal that server's saved deployment inputs. -- Prefill the deployment editor from this reveal path only after the operator deliberately asks to show the configuration. -- Keep ordinary deployment reads, server lists, job summaries, logs, audits, and plugin-facing reads redacted. -- Add a safe deployment-input diagnostic that proves whether the Platform job dispatched to Run carried the saved deployment revision without putting raw values into job/status views. - -## Capabilities - -### New Capabilities - -- `server-deployment-input-reveal`: Authorized, explicit inspection of a server's saved deployment inputs and their Run-dispatch state. - -### Modified Capabilities - -- None. - -## Impact - -- Affected API: server deployment routes and DTOs. -- Affected platform code: deployment service, repository-backed deployment data, lifecycle dispatch diagnostics, and API tests. -- Affected frontend: the shared server deployment workflow and API types/client. -- Affected external integration: Run job-input verification remains based on the existing leased deployment contract; no Run source or protocol expansion is required. diff --git a/openspec/changes/reveal-server-deployment-inputs/specs/server-deployment-input-reveal/spec.md b/openspec/changes/reveal-server-deployment-inputs/specs/server-deployment-input-reveal/spec.md deleted file mode 100644 index 1a0aea1..0000000 --- a/openspec/changes/reveal-server-deployment-inputs/specs/server-deployment-input-reveal/spec.md +++ /dev/null @@ -1,37 +0,0 @@ -## ADDED Requirements - -### Requirement: Authorized deployment input reveal -The Platform SHALL provide an explicit authenticated read operation that lets an authorized server manager retrieve the saved server root, working directory, install command, start command, stop command, and status command for one server deployment. The operation MUST NOT return runtime bindings. - -#### Scenario: Owner reveals a custom deployment -- **WHEN** an authorized server manager explicitly requests deployment input reveal for a stopped server with saved custom commands -- **THEN** the Platform returns the saved deployment paths and commands for that server only - -#### Scenario: Unauthorized user requests reveal -- **WHEN** a user without access to the server requests deployment input reveal -- **THEN** the Platform rejects the request and returns no deployment input - -### Requirement: Default deployment views remain redacted -The existing server deployment read endpoint and all list, detail, job, audit, log, and plugin-facing projections SHALL remain redacted after reveal support is added. - -#### Scenario: Normal deployment read after reveal support -- **WHEN** an authorized user reads a configured deployment through the existing deployment endpoint -- **THEN** the response indicates configured state without returning paths or commands - -### Requirement: Editor reveal is deliberate and bounded -The deployment editor SHALL start with protected inputs redacted and SHALL fetch saved path and command values only after the operator selects its explicit reveal control. Closing the editor MUST discard revealed values from its local form state. - -#### Scenario: Operator opens editor without revealing -- **WHEN** an operator opens an existing deployment editor -- **THEN** protected input fields remain blank and show their configured state - -#### Scenario: Operator explicitly reveals inputs -- **WHEN** an operator selects the reveal control in the open deployment editor -- **THEN** the editor displays the returned saved paths and commands for inspection and editing - -### Requirement: Dispatch diagnostics distinguish Platform dispatch from Run execution -The deployment view SHALL expose safe diagnostic metadata for the most recent lifecycle job: job identifier, job state, deployed revision, and whether the leased input included a deployment definition. It MUST NOT expose the leased values. - -#### Scenario: Platform queued a deployment definition -- **WHEN** Platform creates a lifecycle job from a saved deployment definition -- **THEN** the deployment diagnostic identifies the job revision and that the job included a deployment definition without returning its paths or commands diff --git a/openspec/changes/reveal-server-deployment-inputs/tasks.md b/openspec/changes/reveal-server-deployment-inputs/tasks.md deleted file mode 100644 index 9edfe42..0000000 --- a/openspec/changes/reveal-server-deployment-inputs/tasks.md +++ /dev/null @@ -1,22 +0,0 @@ -## 任务提示 - -- 正向提示词:为“服务器管理”提供已授权管理者主动查看和编辑已保存部署路径/命令的能力,并显示不泄露原文的 Platform→Run 调度证据。成功标准是显式展示可用、常规读取仍脱敏、测试可证明授权和调度行为。 -- 方向提示词:在 `platform/` 增加受限 reveal DTO、路由、服务和测试;在 `platform_web/` 的既有 `ServerDeploymentWorkflow` 内增加显式展示控件,复用现有控制台样式。验证运行 Go/前端测试、`openspec validate --strict` 和 `scripts/check-structure.sh`。 -- 任务边界:不修改独立 `run/` 源码或协议;不增加 SSH、主机浏览、日志/审计原文泄露、插件访问或运行绑定凭据展示;不触及未相关根目录或既有用户改动。 - -## 1. Platform reveal and dispatch diagnostics - -- [x] 1.1 Define reveal and safe dispatch-diagnostic domain/DTO contracts, then add the authorized reveal endpoint without changing existing redacted views. -- [x] 1.2 Derive safe latest-lifecycle-job dispatch evidence from the stored job record and include it in the normal deployment view. -- [x] 1.3 Add service/API tests for owner reveal, unauthorized denial, existing-view redaction, and dispatched deployment evidence. - -## 2. Deployment editor reveal - -- [x] 2.1 Add API client/types for the explicit reveal operation and safe dispatch diagnostic. -- [x] 2.2 Add an intentional reveal control to the shared deployment editor, populate returned fields, and clear the displayed values when it closes. -- [x] 2.3 Add frontend tests that retain the normal redacted opening state and cover explicit reveal behavior. - -## 3. Verification - -- [x] 3.1 Run focused backend and frontend validation, strict OpenSpec validation, and the repository structure check. -- [x] 3.2 Inspect the active Platform metadata and lifecycle-job evidence to report whether Run received the saved deployment definition without exposing raw values in the report. diff --git a/openspec/changes/secure-single-file-run-distribution/.openspec.yaml b/openspec/changes/secure-single-file-run-distribution/.openspec.yaml deleted file mode 100644 index 9e5b8a1..0000000 --- a/openspec/changes/secure-single-file-run-distribution/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-23 diff --git a/openspec/changes/secure-single-file-run-distribution/design.md b/openspec/changes/secure-single-file-run-distribution/design.md deleted file mode 100644 index dcf1bf3..0000000 --- a/openspec/changes/secure-single-file-run-distribution/design.md +++ /dev/null @@ -1,60 +0,0 @@ -## Context - -The existing distribution pipeline already queues real `distribution.build` jobs and keeps raw component keys out of platform APIs. The weak point is the generated package shape: Run artifacts still contain `config.json`, which makes accidental token disclosure easy when a ZIP is shared or inspected. The operator expectation is also a single `run.exe` on Windows, not an archive. - -## Goals / Non-Goals - -**Goals:** - -- Produce Windows Run downloads as `run-windows-amd64.exe` and Linux Run downloads as raw executable files such as `run-linux-amd64`. -- Compile server identity and the current Run key into the Run binary using Go native `-ldflags -X`. -- Keep `RUN_PLATFORM_URL` and other explicit environment overrides working for local development and diagnostics. -- Support raw-executable Run self-update artifacts with checksum verification and the existing staging/rollback flow. -- Make run-key reset reachable from the server list while preserving the compact action popover. -- Immediately revoke an online Run control session after its key is reset. - -**Non-Goals:** - -- Do not add device authorization, enrollment binding, or a token exchange ceremony. -- Do not claim compile-time embedded tokens are unrecoverable from the executable; possession of the executable remains a trust boundary. -- Do not change Client Manager packaging or plugin-declared client-manager build semantics. -- Do not modify billing, cloud host sales, AI provider, plugin marketplace, or unrelated server workflows. - -## Decisions - -### Decision 1: Compile-time Run identity is a build-input contract - -Platform extends authenticated distribution build input with the public Run platform URL and the existing identity fields. The trusted Run worker passes those values to `go build -ldflags -X browser.local/run/config.=`. Run config loading prefers explicit environment variables, then compile-time build values, then developer defaults. - -This uses the Go-native mechanism the user requested. It avoids `go:embed`, generated source files, temporary code rewrites, or sidecar config files for Run. The compile-time value is still recoverable by someone holding the binary, but it removes the casual ZIP/config leak. - -### Decision 2: Raw executable is a Run-only package format - -Run distributions use a new `raw-executable` package format. The worker uploads the compiled binary bytes directly and does not call the archive writer for Run. Client-manager distributions keep the existing ZIP/tar.gz packaging and config injection because they are plugin-declared companion builds with their own lifecycle. - -### Decision 3: Self-update treats raw executable as first-class - -Run update input may carry `raw-executable`. The self-update executor still downloads through the artifact channel, verifies the full artifact checksum, writes the staged executable under the transaction workspace, records its binary checksum, and uses the existing activation and rollback logic. - -### Decision 4: Run keys get a dedicated generator - -The existing `randomToken()` remains 32 random bytes because it is also used for auth sessions, user ID suffixes, and job leases. Run component keys use a new 64-byte URL-safe generator, increasing key length only for Run authorization. - -### Decision 5: Reset revokes the active Run session - -The reset service already revokes old distribution artifacts and increments key generation. This change also removes the active Run control session for that server endpoint when the Run component key is reset, forcing a freshly compiled binary to authenticate before more control/job traffic is accepted. - -## Risks / Trade-offs - -- Embedded authorization can be extracted from a binary by a determined operator or attacker with file access. This is acceptable for the requested distribution model and is explicitly not a DRM or device-binding system. -- Compile-time values can be visible to privileged users on the build worker. The build worker remains part of the trusted platform boundary. -- Raw executables lose the convenience of multi-file package payloads, so any future service installer/systemd wrapper should be a separate explicit distribution profile rather than hidden in this change. - -## Verification - -- `openspec validate secure-single-file-run-distribution --strict` -- `cd run && go test ./...` -- `cd platform && go test ./...` -- `cd platform_web && npm test` -- `cd platform_web && npm run typecheck` -- `scripts/check-structure.sh` diff --git a/openspec/changes/secure-single-file-run-distribution/proposal.md b/openspec/changes/secure-single-file-run-distribution/proposal.md deleted file mode 100644 index c8ac49d..0000000 --- a/openspec/changes/secure-single-file-run-distribution/proposal.md +++ /dev/null @@ -1,29 +0,0 @@ -## Why - -Run distributions currently produce a secret-bearing archive with a sidecar `config.json`, and downloaded Windows packages are ZIP files. Operators need the platform to deliver one server-scoped executable whose platform URL and runtime authorization are compiled into the binary. - -## What Changes - -- Change platform-managed Run distributions to publish a single raw executable instead of a ZIP/tarball plus `config.json`. -- Inject Run platform URL, worker mode, runtime identity, key generation, and authorization token through Go `-ldflags -X` during the trusted Run build. -- Increase Run component key entropy without changing the global session/job token generator. -- Allow Run self-update jobs to consume raw executable artifacts for Windows and Linux targets. -- Add run-key reset to the server-list "运行操作" dangerous menu, using the existing reset API. -- Revoke the active Run control session when the run key is reset so old deployed binaries stop immediately. - -## Capabilities - -### New Capabilities - -- `secure-single-file-run-distribution`: Server-scoped Run executable distribution, compile-time authorization injection, raw-binary self-update, and list-level run-key reset. - -### Modified Capabilities - -- None. - -## Impact - -- Affects `platform/` distribution build input, run-key generation/reset behavior, artifact naming, package-format validation, and tests. -- Affects `run/` config loading, distribution build packaging, self-update extraction, protocol DTOs, and tests. -- Affects `platform_web/` server-list runtime action menu and tests. -- Does not touch plugins, client-manager package format, AI provider flows, billing/cloud features, or unrelated UI systems. diff --git a/openspec/changes/secure-single-file-run-distribution/specs/secure-single-file-run-distribution/spec.md b/openspec/changes/secure-single-file-run-distribution/specs/secure-single-file-run-distribution/spec.md deleted file mode 100644 index f089951..0000000 --- a/openspec/changes/secure-single-file-run-distribution/specs/secure-single-file-run-distribution/spec.md +++ /dev/null @@ -1,73 +0,0 @@ -## ADDED Requirements - -### Requirement: Platform publishes single-file Run executables - -The platform SHALL publish Run distributions as one raw executable per server, target OS, and architecture instead of an archive containing a sidecar configuration file. - -#### Scenario: Operator generates Windows Run - -- **WHEN** an authorized operator generates Run for a Windows target -- **THEN** platform MUST queue a distribution build job that uploads a single executable artifact and presents it as `run-windows-.exe` with `application/octet-stream` -- **AND** the artifact MUST NOT contain a downloadable `config.json` sidecar - -#### Scenario: Operator generates Linux Run - -- **WHEN** an authorized operator generates Run for a Linux target -- **THEN** platform MUST queue a distribution build job that uploads a single executable artifact and presents it as `run-linux-` with `application/octet-stream` - -### Requirement: Run authorization is compiled into the executable - -Run distribution builds SHALL inject server-scoped identity and the active Run authorization key into the executable at Go build time. - -#### Scenario: Build input is consumed by trusted Run builder - -- **WHEN** a Run worker receives authenticated distribution build input for component kind `run` -- **THEN** it MUST compile the target with Go `-ldflags -X` values for worker mode, platform URL, run endpoint ID, server instance ID, plugin ID, component kind, key generation, target release, and authorization token -- **AND** explicit runtime environment variables such as `RUN_PLATFORM_URL` MUST remain able to override compiled defaults for local development - -#### Scenario: Compiled executable starts without sidecar config - -- **WHEN** the generated Run executable starts with no `config.json` -- **THEN** it MUST load the compiled identity and key, register as a worker by default, and authenticate against the current platform key generation - -### Requirement: Run keys use increased entropy - -Platform SHALL generate Run component keys with more entropy than general-purpose platform tokens while preserving existing global token behavior. - -#### Scenario: Run key is created or reset - -- **WHEN** platform creates or resets a Run component key -- **THEN** it MUST use a dedicated Run key generator of at least 64 random bytes before URL-safe encoding -- **AND** it MUST NOT change auth session or job lease token generation - -### Requirement: Raw executable self-update is supported - -Run self-update SHALL accept raw executable artifacts for supported Run targets. - -#### Scenario: Raw update artifact is staged - -- **WHEN** Run receives a self-update job whose package format is `raw-executable` -- **THEN** Run MUST download the artifact through the artifact channel, verify the full checksum, stage it as an executable file, record the staged binary checksum, and use the existing activation/rollback flow - -#### Scenario: Unsupported update package is requested - -- **WHEN** Run receives a self-update job with an unsupported package format -- **THEN** Run MUST reject the update before activation and keep the current executable - -### Requirement: Server list exposes run-key reset - -The server-list runtime action menu SHALL expose run-key reset as a destructive runtime operation when the platform reports it available. - -#### Scenario: Operator resets from server list - -- **WHEN** an authorized operator chooses run-key reset from the server-list `运行操作` menu and confirms the destructive action -- **THEN** platform_web MUST call the existing run-key reset API, show a tracked operation result, refresh server state, and indicate that a new Run executable must be generated - -### Requirement: Run key reset revokes active control sessions - -Resetting a Run key SHALL immediately invalidate the active Run control session for the server's assigned Run endpoint. - -#### Scenario: Online Run key is reset - -- **WHEN** platform successfully resets the Run key for a server instance with an active Run control session -- **THEN** platform MUST remove that session so old deployed executables cannot continue heartbeat or job traffic under the stale key diff --git a/openspec/changes/secure-single-file-run-distribution/tasks.md b/openspec/changes/secure-single-file-run-distribution/tasks.md deleted file mode 100644 index b68b5f8..0000000 --- a/openspec/changes/secure-single-file-run-distribution/tasks.md +++ /dev/null @@ -1,47 +0,0 @@ -## Prompt Boundaries - -正向提示词: Deliver a secure single-file Run distribution flow for 服务器管理, where generated Run artifacts are raw executables with compile-time platform URL and authorization, self-update accepts raw executables, and server-list run-key reset is available with confirmation. - -方向提示词: Preserve existing platform/run/platform_web boundaries; use platform distribution build input and Run Go `-ldflags -X` injection; keep Client Manager packaging unchanged; verify with focused Go/frontend tests, OpenSpec validation, and `scripts/check-structure.sh`. - -任务边界: Do not add device authorization binding, billing, cloud host sales, AI-provider changes, plugin marketplace expansion, Client Manager packaging changes, raw credential exposure in APIs/UI, or unrelated visual-system changes. - -## 1. OpenSpec - -- [x] 1.1 Define proposal, design, requirements, and task boundaries for single-file Run distribution. -- [x] 1.2 Validate the OpenSpec change before implementation completion. - -## 2. Platform - -- [x] 2.1 Add Run-only raw executable package format, artifact filename/content-type presentation, and build-input platform URL. -- [x] 2.2 Generate longer Run component keys without changing global token generation. -- [x] 2.3 Revoke active Run control sessions when Run key reset succeeds. -- [x] 2.4 Update platform tests for raw Run artifacts, key length, build input, reset revocation, and self-update input. - -## 3. Run - -- [x] 3.1 Add compile-time config variables with environment override precedence. -- [x] 3.2 Inject Run identity and authorization through Go `-ldflags -X` and upload raw executable bytes for Run builds. -- [x] 3.3 Support raw executable self-update staging while preserving archive handling for compatibility/tests where needed. -- [x] 3.4 Update Run tests for raw build output, compiled smoke identity, config precedence, and raw self-update. - -## 4. platform_web - -- [x] 4.1 Add run-key reset to the server-list `运行操作` dangerous menu with confirmation and API execution. -- [x] 4.2 Update frontend tests and fixtures for raw Run download metadata and reset action coverage. - -## 5. Verification - -- [x] 5.1 Run focused `run`, `platform`, and `platform_web` verification. -- [x] 5.2 Run `scripts/check-structure.sh`. - - -## Verification Evidence - -- `openspec validate secure-single-file-run-distribution --strict`: passed -- `cd platform && go test ./service -run 'TestCoreService(ResetRunKey|GeneratesRun|RunDistribution|DistributionBuild)' -count=1`: passed -- `cd platform && go test ./validator ./domain ./dto -count=1`: passed -- `cd run && go test ./config ./protocol ./runtime -run 'TestLoad|TestWorkerDistribution|TestDistributionBuild|TestValidateDistribution|TestRunSelfUpdate|TestPrepareSelfUpdate|TestWorkerDispatchesSelfUpdate' -count=1`: passed -- `cd platform_web && npm test -- --run pages/ConsolePages.test.tsx api/client.test.ts`: passed, 2 files / 25 tests -- `scripts/check-structure.sh`: passed -- Note: full `run/runtime` suite still needs network bind permissions for unrelated Source RCON/httptest fixtures; focused distribution/self-update coverage was used for this change. diff --git a/openspec/changes/separate-server-run-binding/.openspec.yaml b/openspec/changes/separate-server-run-binding/.openspec.yaml deleted file mode 100644 index 3a03821..0000000 --- a/openspec/changes/separate-server-run-binding/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-25 diff --git a/openspec/changes/separate-server-run-binding/design.md b/openspec/changes/separate-server-run-binding/design.md deleted file mode 100644 index 9f26791..0000000 --- a/openspec/changes/separate-server-run-binding/design.md +++ /dev/null @@ -1,59 +0,0 @@ -## Context - -`ServerInstance.RunEndpointID` currently points at a global endpoint selected while creating the server. That endpoint both executes the initial install/build jobs and is later treated as the endpoint for a generated, server-scoped Run. The type has no server ownership, registration accepts any endpoint ID after validating only the component key, and reset revokes the shared endpoint session. The browser consequently asks for a Run before the server-specific Run exists. - -## Goals / Non-Goals - -**Goals:** - -- Keep a server bound to exactly one dedicated Run endpoint after registration. -- Preserve an explicit deployment target for target OS, architecture, and trusted build work without treating it as the server Run. -- Make draft creation, reserved endpoint identity, Run registration, deployment, replacement, and revocation stateful and auditable. -- Prevent authenticated Run binaries from claiming another server endpoint or jobs. - -**Non-Goals:** - -- Adding host provisioning, SSH, a second executor source tree, or browser access to host paths and credentials. -- Migrating already-active legacy server endpoints automatically; they remain readable and are handled through an explicit future migration. -- Changing plugin runtime-profile, channel isolation, or artifact confidentiality rules. - -## Decisions - -### 1. Store deployment target and dedicated Run separately - -`ServerInstance` gains `DeploymentTargetID` while `RunEndpointID` is reserved for the dedicated endpoint. A server draft can have a target but no Run endpoint. The target must be an online compatible endpoint with `distribution.build`; it is used only to build the dedicated Run package. Lifecycle deployment and subsequent runtime work require the dedicated endpoint. - -This avoids overloading an existing field. Reusing `RunEndpointID` with a role enum would leave historical jobs and control sessions ambiguous. - -### 2. Reserve deterministic endpoint ownership before package generation - -The dedicated Run endpoint ID is deterministic (`server-run-`). Package generation persists that reserved identity. A component-authenticated hello must use this identity, name the same server and Run component, and is rejected if the endpoint belongs to another server. Control registration records the ownership only after all checks pass. - -This avoids a browser-chosen endpoint ID and permits Run to self-register without exposing a registration credential separate from the component key. - -### 3. Draft first; deployment is explicit - -The create workflow always creates a `draft`; choosing a deployment target does not queue installation. After a Run package is generated, downloaded, and registers online, the operator explicitly deploys the definition. This removes the bootstrap loop and keeps all execution behind an authenticated Run. - -### 4. Use a focused additive migration - -The new fields are additive. Legacy instances with a populated `RunEndpointID` and no `DeploymentTargetID` keep their existing behavior through a compatibility branch. New server workflow requests use the new draft path. A later migration can opt legacy records into exclusive ownership only after operational review. - -## Risks / Trade-offs - -- [A build target must already exist] → The UI calls it a deployment target and explains that it is a trusted build worker, not the dedicated Run. -- [Legacy endpoints can be shared] → New exclusive ownership checks apply only to newly reserved endpoints; legacy records are not silently broken. -- [Registration failure leaves a package unusable] → The server remains a recoverable draft with a redacted reason and can regenerate/reset the Run key. -- [Run repository must adopt the identity] → Document the revised hello contract and gate deployment until matching registration occurs. - -## Migration Plan - -1. Add additive domain/model/DTO fields and persistence compatibility. -2. Create new servers as target-bound drafts and generate packages through their deployment target. -3. Require reserved identity checks at component hello and only dispatch deployment to the dedicated endpoint. -4. Update the independent Run implementation to send the reserved endpoint ID, then enable the new workflow in environments that have it. -5. Roll back by leaving new servers as drafts; no browser or platform fallback executes work directly on a target host. - -## Open Questions - -- The independent Run repository must consume the returned/reserved endpoint identity before an end-to-end real-machine rollout. diff --git a/openspec/changes/separate-server-run-binding/proposal.md b/openspec/changes/separate-server-run-binding/proposal.md deleted file mode 100644 index b4b83e5..0000000 --- a/openspec/changes/separate-server-run-binding/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -The current server workflow uses one `RunEndpoint` both as a pre-existing worker that builds and installs software and as the server-scoped Run binary generated after creation. This creates a bootstrap loop, permits multiple servers to share one endpoint despite the intended one-server/one-Run model, and leaves component registration insufficiently bound to its server. - -## What Changes - -- Add a server-scoped Run binding with an explicit lifecycle from draft, through target selection and Run registration, to deployment. -- **BREAKING** Separate a deployment target from the dedicated Run endpoint that controls one server; a selected target no longer immediately dispatches installation. -- Require a generated Run to register with the endpoint identity reserved for its server before deployment or runtime jobs can be dispatched. -- Reject endpoint reuse, mismatched component registration, and cross-server job access; revoke only the Run bound to the affected server. -- Update the server creation workflow to save a draft, describe the selected target accurately, and guide the operator through Run generation, registration, and deployment. - -## Capabilities - -### New Capabilities - -- `server-scoped-run-binding`: Securely reserve, register, validate, replace, and revoke one dedicated Run endpoint for a server instance. -- `server-run-bootstrap-workflow`: Create a server as a draft and progress it through deployment-target selection, dedicated Run registration, and explicit deployment. - -### Modified Capabilities - -- None. - -## Impact - -- `platform/`: domain, DTOs, models/repositories, services, control registration, lifecycle dispatch, distribution build selection, validation, API documentation, and tests. -- `platform_web/`: API types, server creation/deployment workflow, Run builder status and focused tests. -- `run/`: independent repository coordination is required for the revised registration identity; this repository will only update its public contract. diff --git a/openspec/changes/separate-server-run-binding/specs/server-run-bootstrap-workflow/spec.md b/openspec/changes/separate-server-run-binding/specs/server-run-bootstrap-workflow/spec.md deleted file mode 100644 index 4fe3764..0000000 --- a/openspec/changes/separate-server-run-binding/specs/server-run-bootstrap-workflow/spec.md +++ /dev/null @@ -1,19 +0,0 @@ -## ADDED Requirements - -### Requirement: Server creation is a draft-to-deployment workflow -The management console SHALL create a server definition as a draft, even when a deployment target is selected. It MUST NOT dispatch installation until the dedicated Run has registered and the operator explicitly deploys. - -#### Scenario: Create with a deployment target -- **WHEN** an operator selects a compatible deployment target and confirms server creation -- **THEN** the console reports a saved draft and guides the operator to generate and register the dedicated Run - -#### Scenario: Deploy before Run registration -- **WHEN** an operator tries to deploy a draft before its dedicated Run is online -- **THEN** the platform rejects the request with a safe registration-required reason and dispatches no job - -### Requirement: The console distinguishes target from dedicated Run -The console SHALL label the initial selection as a deployment target and SHALL explain that it is a trusted build target, not the server's dedicated Run. - -#### Scenario: Review server creation -- **WHEN** an operator reviews a new server definition -- **THEN** the review identifies the deployment target and says that a dedicated Run must be generated and registered before deployment diff --git a/openspec/changes/separate-server-run-binding/specs/server-scoped-run-binding/spec.md b/openspec/changes/separate-server-run-binding/specs/server-scoped-run-binding/spec.md deleted file mode 100644 index f2c2816..0000000 --- a/openspec/changes/separate-server-run-binding/specs/server-scoped-run-binding/spec.md +++ /dev/null @@ -1,30 +0,0 @@ -## ADDED Requirements - -### Requirement: A dedicated Run endpoint is exclusively bound to one server -The platform SHALL reserve one dedicated Run endpoint identity for each newly created server and SHALL reject attempts to bind that identity to another server. The deployment target SHALL be stored separately from the dedicated endpoint. - -#### Scenario: A target is selected for a new server -- **WHEN** an operator creates a server with a compatible deployment target -- **THEN** the platform saves the target and a reserved dedicated Run identity on a draft without dispatching an install job - -#### Scenario: A dedicated endpoint is reused -- **WHEN** a caller attempts to register or bind a dedicated endpoint reserved for another server -- **THEN** the platform rejects the request without changing either server binding or control session - -### Requirement: Component hello is constrained to its server binding -The platform SHALL accept a component-authenticated Run hello only when the server ID, component kind, current key generation, and reserved endpoint ID all match the server binding. - -#### Scenario: A generated Run registers correctly -- **WHEN** a generated Run presents the current key and its reserved endpoint identity -- **THEN** the platform records the endpoint online and allows server-scoped work to be dispatched - -#### Scenario: A generated Run claims another endpoint -- **WHEN** a generated Run presents a valid key but an endpoint identity that is not reserved for its server -- **THEN** the platform rejects the hello and does not replace any endpoint session - -### Requirement: Revocation affects only the bound dedicated Run -The platform SHALL revoke a Run control session only when the endpoint is exclusively bound to the server whose Run key is reset. - -#### Scenario: Reset a dedicated Run key -- **WHEN** an operator resets a server Run key -- **THEN** the platform revokes that server's dedicated Run session and leaves unrelated server endpoints unchanged diff --git a/openspec/changes/separate-server-run-binding/tasks.md b/openspec/changes/separate-server-run-binding/tasks.md deleted file mode 100644 index 4d0e964..0000000 --- a/openspec/changes/separate-server-run-binding/tasks.md +++ /dev/null @@ -1,20 +0,0 @@ -## 1. Server binding contract - -- [x] 1.1 Add deployment-target and dedicated-Run ownership fields to domain, persistence, DTO, and safe server projections. -- [x] 1.2 Validate exclusive dedicated endpoint ownership and draft/deployment state transitions. - -## 2. Secure lifecycle and control behavior - -- [x] 2.1 Create target-bound drafts, reserve their dedicated Run identity, and dispatch deployment only to a registered dedicated Run. -- [x] 2.2 Build Run distributions through the deployment target while recording the reserved dedicated endpoint identity. -- [x] 2.3 Constrain component hello and revocation to the matching server-owned endpoint. - -## 3. Management console workflow - -- [x] 3.1 Update API types and the creation workflow to label deployment targets and save target-bound drafts. -- [x] 3.2 Guide draft operators through dedicated Run registration before deployment and update focused UI tests. - -## 4. Verification - -- [x] 4.1 Add backend tests for exclusive ownership, registration mismatch rejection, scoped revocation, and draft deployment gating. -- [x] 4.2 Run formatting, focused tests, full structural validation, strict OpenSpec validation, and build/type checks. diff --git a/openspec/changes/split-run-into-independent-repository/.openspec.yaml b/openspec/changes/split-run-into-independent-repository/.openspec.yaml deleted file mode 100644 index 64105fc..0000000 --- a/openspec/changes/split-run-into-independent-repository/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-14 diff --git a/openspec/changes/split-run-into-independent-repository/design.md b/openspec/changes/split-run-into-independent-repository/design.md deleted file mode 100644 index 2b04b4c..0000000 --- a/openspec/changes/split-run-into-independent-repository/design.md +++ /dev/null @@ -1,27 +0,0 @@ -# Design - -## Repository Boundary - -The browser repository remains responsible for `platform/`, `platform_web/`, and `plugins/`. The machine-side executor implementation is owned by `git@git.npc0.com:admin343/run.git`. - -Browser-side code can still expose platform APIs for run endpoints, jobs, logs, artifacts, and runtime package distribution. Those are platform contracts, not embedded run implementation code. - -## Local Development - -Local debug workflows use `RUN_REPO_DIR` to find an external run checkout. The default points to `./run`, an ignored nested checkout inside this repository: - -```bash -git clone git@git.npc0.com:admin343/run.git run -``` - -If `RUN_REPO_DIR` is missing or does not contain a run `go.mod`, local debug scripts fail with a clear setup message. - -Local debug start/stop scripts treat pid files as hints, not the only source of truth. If a previous local debug process still owns the configured platform or web port after pid files are removed, the scripts identify it by cwd/log ownership under this repository and stop it before starting a new stack. - -## Docker Compose - -The compose file keeps the run service for all-in-one local deployment, but its build context points at `${RUN_REPO_DIR:-./run}` and uses the external repository's `Dockerfile`. - -## Structure Validation - -`scripts/check-structure.sh` validates browser-owned roots only. It no longer requires run source paths. diff --git a/openspec/changes/split-run-into-independent-repository/proposal.md b/openspec/changes/split-run-into-independent-repository/proposal.md deleted file mode 100644 index 97c8158..0000000 --- a/openspec/changes/split-run-into-independent-repository/proposal.md +++ /dev/null @@ -1,21 +0,0 @@ -# Split run into independent repository - -## Summary - -Remove the `run/` source tree from the browser repository now that the executor lives in `git@git.npc0.com:admin343/run.git`. - -## Motivation - -The run executor has its own repository and release boundary. Keeping the full source tree duplicated inside `browser.git` creates two owners for the same implementation and makes future commits ambiguous. - -## Scope - -- Remove tracked `run/` files from this repository. -- Update root governance, README, Docker Compose, local debug scripts, and structure checks to treat run as an external checkout. -- Keep platform-side run protocol/API contracts in `platform/` because browser still owns the platform control plane. - -## Out of Scope - -- Changing run protocol semantics. -- Moving platform API routes or frontend run management screens. -- Rewriting historical OpenSpec records that describe earlier monorepo milestones. diff --git a/openspec/changes/split-run-into-independent-repository/specs/project-workspace-governance/spec.md b/openspec/changes/split-run-into-independent-repository/specs/project-workspace-governance/spec.md deleted file mode 100644 index c31534e..0000000 --- a/openspec/changes/split-run-into-independent-repository/specs/project-workspace-governance/spec.md +++ /dev/null @@ -1,19 +0,0 @@ -## ADDED Requirements - -### Requirement: Browser repository uses external run implementation -The browser repository SHALL NOT store the machine-side run executor source tree. The run executor implementation SHALL live in the independent repository `git@git.npc0.com:admin343/run.git`. - -#### Scenario: Browser root is inspected -- **WHEN** a contributor lists first-class implementation roots in the browser repository -- **THEN** the roots MUST be `platform/`, `platform_web/`, and `plugins/` -- **AND** `run/` MUST NOT be required as a browser-owned source directory - -#### Scenario: Local debug needs a run worker -- **WHEN** a local debug or Docker workflow needs to start run -- **THEN** it MUST locate run through `RUN_REPO_DIR` or the documented ignored nested checkout -- **AND** it MUST fail with a clear setup message when the external checkout is missing - -#### Scenario: Structure validation runs -- **WHEN** `scripts/check-structure.sh` runs in the browser repository -- **THEN** it MUST validate browser-owned platform, frontend, plugin, and governance paths -- **AND** it MUST NOT require files under `run/` diff --git a/openspec/changes/split-run-into-independent-repository/tasks.md b/openspec/changes/split-run-into-independent-repository/tasks.md deleted file mode 100644 index 72ef3a1..0000000 --- a/openspec/changes/split-run-into-independent-repository/tasks.md +++ /dev/null @@ -1,9 +0,0 @@ -# Tasks - -- [x] Add OpenSpec proposal/design/spec for the repository split. -- [x] Remove `run/` from browser-owned project roots and structure checks. -- [x] Update README, local debug scripts, and Docker Compose to use `RUN_REPO_DIR`. -- [x] Harden local debug start/stop against stale owned listeners when pid files are missing. -- [x] Remove tracked `run/` source files from `browser.git`. -- [x] Run `scripts/check-structure.sh`. -- [x] Run `openspec validate split-run-into-independent-repository --strict`. diff --git a/openspec/changes/stream-live-server-logs-sse/design.md b/openspec/changes/stream-live-server-logs-sse/design.md deleted file mode 100644 index c9563fc..0000000 --- a/openspec/changes/stream-live-server-logs-sse/design.md +++ /dev/null @@ -1,15 +0,0 @@ -## Overview - -Realtime browser log display is a one-way stream, so the platform exposes Server-Sent Events instead of WebSocket for this change. SSE gives the browser one long-lived HTTP response, works with standard `EventSource`, carries same-origin HttpOnly session cookies, and only needs Nginx buffering disabled. - -## Transport Boundary - -Run continues to upload logs through `POST /api/v1/run/logs/batches`. That path remains durable and retryable: Run writes to local spool, sends bounded batches, receives sequence ACKs, and can retry without depending on browser presence. - -Run log capture remains plugin-declared. For live terminal output, Run captures the supervised process channels declared by the plugin as `process.stdout` / `process.stderr`; game file tails such as `file.tail` are explicit plugin-declared history/backfill sources rather than a generic default. A managed process started by Run writes stdout/stderr into Run-owned capture files and Run tails those capture files into durable batch ingest, so restarting Run can resume transmission for an already-running supervised process without inspecting game-specific logs such as `SCUM.log`. - -The browser subscribes to `GET /api/v1/server-instances/{id}/logs/events`. Platform authorizes the user session against the server instance, replays a bounded recent history per stream, then publishes newly ingested log entries from memory fan-out. `POST /api/v1/log-streams/query` stays as an explicit historical cursor API, not a realtime polling loop. - -## Proxy Notes - -SSE does not require `Upgrade` or `Connection: upgrade`. Reverse proxies must avoid buffering the stream and should keep the upstream read timeout long enough for idle log periods. diff --git a/openspec/changes/stream-live-server-logs-sse/proposal.md b/openspec/changes/stream-live-server-logs-sse/proposal.md deleted file mode 100644 index 8422cab..0000000 --- a/openspec/changes/stream-live-server-logs-sse/proposal.md +++ /dev/null @@ -1,16 +0,0 @@ -## Why - -The browser terminal and server log detail view were polling `POST /api/v1/log-streams/query` on a short interval, and terminal polling multiplied that request count by every candidate log stream. This wastes HTTP requests and can overload the platform or reverse proxy while still failing to feel truly realtime. - -## What Changes - -- Add a platform-owned `GET /api/v1/server-instances/{id}/logs/events` Server-Sent Events stream for browser live logs. -- Keep Run-to-Platform log transport as durable signed HTTP batch ingest with local spool, sequence acknowledgement, and cursor query for history/reconnect repair. -- Switch the server detail log view, live log drawer, and management terminal to a single EventSource connection with bounded initial history instead of periodic `/log-streams/query` polling. -- Add Nginx proxy settings for unbuffered SSE forwarding. - -## Impact - -- Affects `platform/` API, DTO, service log ingest fan-out, tests, and protocol docs. -- Affects `platform_web/` API types/client, log UI components, tests, and Nginx config. -- Does not add WebSocket terminal transport, direct browser-to-Run connections, plugin-held platform keys, host paths, credentials, or game-specific log behavior in platform/run. diff --git a/openspec/changes/stream-live-server-logs-sse/specs/browser-live-log-streaming/spec.md b/openspec/changes/stream-live-server-logs-sse/specs/browser-live-log-streaming/spec.md deleted file mode 100644 index 2b3be03..0000000 --- a/openspec/changes/stream-live-server-logs-sse/specs/browser-live-log-streaming/spec.md +++ /dev/null @@ -1,52 +0,0 @@ -## ADDED Requirements - -### Requirement: Browser live logs use a platform push stream -The Platform SHALL provide a server-scoped browser log event stream that sends safe log stream metadata and log entries over Server-Sent Events. - -#### Scenario: Operator opens live logs -- **WHEN** an authorized operator opens a server log view or management terminal -- **THEN** platform_web opens `GET /api/v1/server-instances/{id}/logs/events` with `EventSource` -- **AND** the view does not start a periodic `/api/v1/log-streams/query` polling loop - -#### Scenario: Initial history is replayed -- **WHEN** the browser opens the log event stream with a bounded `historyLimit` -- **THEN** Platform replays recent stored entries for the server's log streams before sending the ready event -- **AND** each event contains only safe stream metadata and log entry fields - -### Requirement: Durable Run log ingest remains independent -Run-to-Platform log transfer SHALL remain durable HTTP batch ingest with local spool and sequence acknowledgement. Browser streaming MUST fan out only from platform-ingested log data. - -#### Scenario: Run uploads a batch -- **WHEN** Run uploads a valid contiguous log batch -- **THEN** Platform stores it, updates the stream latest sequence, acknowledges the batch, and publishes the new entries to matching browser subscribers -- **AND** duplicate batch acknowledgements do not publish duplicate browser events - -### Requirement: Run live logs follow plugin-declared process channels -Run live terminal output SHALL come from plugin-declared log sources and the supervised process that Run started. Game-specific file logs MUST NOT be used as the default live terminal source unless the plugin declares that file source for explicit history, fallback, or backfill. - -#### Scenario: Run starts a supervised process -- **WHEN** Run executes a plugin lifecycle start action in supervised mode -- **THEN** Run captures the process stdout and stderr into Run-owned durable capture files -- **AND** Run tails those capture files into durable log batch ingest using the plugin-declared process stream keys -- **AND** Run hides the managed Windows process window when the OS supports hidden startup - -#### Scenario: Run restarts while the game process remains alive -- **WHEN** Run restarts and reloads its persisted process journal for an already-running supervised process -- **THEN** Run resumes tailing the Run-owned stdout/stderr capture files from persisted offsets -- **AND** Run does not inspect game-specific logs such as `SCUM.log` to synthesize terminal output - -### Requirement: Browser log streaming uses platform session authorization -The log event stream SHALL be authorized by the current platform user session and server access rules. Plugins and browser code MUST NOT receive Run session tokens, component keys, host paths, raw credentials, or direct Run socket information. - -#### Scenario: Unauthorized user subscribes -- **WHEN** a user without access opens a server log event stream -- **THEN** Platform rejects the request using the existing authorization error behavior -- **AND** no log entries or stream metadata are sent - -### Requirement: Reverse proxies forward log events without buffering -The deployed platform_web reverse proxy SHALL forward the log event route without response buffering and with a long read timeout so idle log periods do not force browser polling. - -#### Scenario: Nginx proxies SSE -- **WHEN** Nginx forwards `/api/v1/server-instances/{id}/logs/events` -- **THEN** buffering is disabled for that location -- **AND** the route does not require WebSocket upgrade headers diff --git a/openspec/changes/stream-live-server-logs-sse/tasks.md b/openspec/changes/stream-live-server-logs-sse/tasks.md deleted file mode 100644 index 73ae54d..0000000 --- a/openspec/changes/stream-live-server-logs-sse/tasks.md +++ /dev/null @@ -1,29 +0,0 @@ -## Prompt Boundaries - -- Positive prompt (正向提示词): Realtime server logs and the management terminal must use one platform-owned push stream per open view, with bounded history replay and no periodic `/log-streams/query` polling. -- Directional prompt (方向提示词): Work inside `platform/`, `platform_web/`, and `run/`, preserve durable Run log ingest, plugin-declared log-source ownership, platform session authorization, existing UI controls, and Nginx proxying; verify with focused Go/frontend tests plus structure checks. -- Boundary prompt (任务边界): Do not add browser-to-Run sockets, raw credentials, host paths, plugin-owned auth keys, billing/cloud workflows, or game-specific SCUM paths/commands to platform/run code. - -## 1. Platform SSE Contract - -- [x] 1.1 Add safe log event DTOs and a server-scoped SSE route. -- [x] 1.2 Publish newly accepted log batch entries to non-blocking server-instance subscribers. -- [x] 1.3 Replay bounded recent history on stream open while keeping cursor query available for explicit history. - -## 2. Frontend Realtime Logs - -- [x] 2.1 Add EventSource client support for server log events. -- [x] 2.2 Replace live log drawer and management terminal `/log-streams/query` intervals with SSE. -- [x] 2.3 Replace server detail log polling with SSE history replay and live append. - -## 3. Proxy And Verification - -- [x] 3.1 Disable Nginx buffering for the log events route. -- [x] 3.2 Add focused backend/frontend tests for SSE behavior and no terminal polling. -- [x] 3.3 Run final verification: platform Go tests, platform_web tests/typecheck, `scripts/check-structure.sh`, and strict OpenSpec validation. - -## 4. Run Process Log Capture - -- [x] 4.1 Capture supervised process stdout/stderr through Run-owned durable capture files instead of game-specific log inspection. -- [x] 4.2 Resume managed process log tailing after Run restart using the persisted process journal and capture offsets. -- [x] 4.3 Keep plugin-declared log source keys optional for runtime readiness; explicit file backfill still uses the requested plugin source. diff --git a/openspec/changes/support-custom-server-deployment-workflows/.openspec.yaml b/openspec/changes/support-custom-server-deployment-workflows/.openspec.yaml deleted file mode 100644 index 5e6d53a..0000000 --- a/openspec/changes/support-custom-server-deployment-workflows/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-24 diff --git a/openspec/changes/support-custom-server-deployment-workflows/design.md b/openspec/changes/support-custom-server-deployment-workflows/design.md deleted file mode 100644 index dbfa703..0000000 --- a/openspec/changes/support-custom-server-deployment-workflows/design.md +++ /dev/null @@ -1,76 +0,0 @@ -## Context - -The platform currently creates an `installing` server instance only when a Run endpoint is already online. It records runtime bindings as safe logical references and dispatches only plugin/profile/action metadata. This prevents self-hosted operators from defining a server before Run is installed, from pointing at an existing absolute directory, and from using a nonstandard command such as a Python virtual environment launcher. - -The independent Run repository remains the only host-side executor. Repository rules forbid returning host paths, raw credentials, or direct sockets from Run to Platform or plugins. - -## Goals / Non-Goals - -**Goals:** - -- Persist an editable server deployment definition without a Run binding. -- Support guided install, existing-server adoption, and custom lifecycle command modes. -- Accept operator-entered absolute paths and command lines as protected write-only execution inputs. -- Render and validate plugin create fields, including port and player-count fields. -- Bind a saved definition to an online Run only at deployment/start time and send Run a versioned, redaction-safe execution plan. -- Surface queued, claimed, preflight, install, configure, start, and health stages to the operator. - -**Non-Goals:** - -- Platform-side SSH, shell execution, direct sockets, or a Run source tree in this repository. -- Cloud hosting, billing, provider marketplaces, or automatic network/firewall provisioning. -- Returning stored full paths, command text, or raw secrets through normal Platform APIs. -- Guaranteeing a generic command works on a node whose Run policy disallows it. - -## Decisions - -### 1. Separate deployment definitions from runtime bindings - -Add a server deployment definition associated with a server instance. It holds deployment mode, plugin create inputs, protected path/command fields, configuration revision, and a binding state. Runtime bindings remain for plugin-declared logical adapters such as RCON and file transports. - -This avoids weakening the existing logical-reference contract simply to accommodate physical deployment input. Reusing runtime bindings would make host paths appear in APIs that intentionally redact those values. - -### 2. A server can exist as an unbound draft - -`draft` is added as an editable server state. A draft has no `runEndpointId`, no queued lifecycle job, and can be created or edited before any Run registers. Binding and deployment are explicit later operations. `installing`, `ready`, `running`, `stopped`, and `failed` retain their existing lifecycle meaning. - -The alternative—requiring a placeholder Run endpoint—would preserve the current coupling and create misleading jobs. - -### 3. Paths and commands are protected write-only fields - -The browser can submit full paths and command text as an operator action. Platform stores them in a protected deployment record and only sends them to the assigned Run via a leased job input. Read APIs return configured flags, a non-sensitive display mode, and a content fingerprint, never the value. Editing a protected value requires resubmission; an empty update preserves the stored value. - -Raw credentials are rejected from commands and must be represented by secret references. This satisfies the host-path redaction rule while supporting real input such as `/srv/server/.venv/bin/python`. - -### 4. Plugin templates are recommendations; custom commands override per lifecycle action - -Plugins publish create-field schemas and optionally map inputs to recommended install/start/stop templates. Guided mode resolves these templates. Existing-server and custom-command modes permit an operator to provide a working directory plus install/start/stop commands; a missing install command is valid for adoption. - -Run receives an argv-oriented command plan by default. A full shell command is allowed only when the operator explicitly selects a shell kind and the Run endpoint advertises the corresponding custom-process policy. This avoids accidental shell interpretation while allowing deliberate venv, batch, PowerShell, and shell-wrapper deployments. - -### 5. Run preflight and lifecycle phases are first-class job progress - -The lifecycle job execution input includes deployment revision, mode, protected plan, create inputs, and a bounded phase vocabulary: `queued`, `claimed`, `preflight`, `install`, `configure`, `start`, and `health`. Run validates paths/executables, policy, port availability, and plugin compatibility before any write. It reports only phase, percent, safe summary, and structured safe error code. - -Platform shows these phases after submit and distinguishes an unclaimed job from a running job. A Run implementation is required in its independent repository; until it supports this input version, Platform must fail safely with an actionable compatibility reason. - -## Risks / Trade-offs - -- [A custom command can be destructive] → Require server-owner/node-operator authorization, Run policy opt-in, explicit shell selection, bounded timeout, command fingerprint audit, and confirmation before dispatch. -- [A path is sensitive operational data] → Treat it as write-only in read models and strip it from logs, job summaries, plugin bridge results, and diagnostics. -- [Existing persisted instances assume a Run endpoint] → Migrate existing records unchanged; only newly created drafts omit it. -- [Run protocol rollout lags Platform] → Version the execution input and make deployment unavailable with a clear compatibility result rather than silently ignoring user input. -- [Port collision cannot be known from Platform] → Validate form shape in Platform, then make the Run preflight authoritative and return its safe diagnostic. - -## Migration Plan - -1. Add deployment definition persistence and draft state while accepting all existing bound instances unchanged. -2. Release Platform/Web support for draft creation and protected deployment updates. -3. Release the versioned contract to Run; enable guided and custom dispatch only after Run reports the deployment-plan capability. -4. Update first-party SCUM and Minecraft templates and add Palworld only as a separate plugin change. -5. Roll back by retaining deployment definitions as drafts and refusing dispatch to incompatible Run versions; no host-side rollback is initiated automatically. - -## Open Questions - -- The independent Run repository must define its exact supported custom shell identifiers and endpoint policy advertisement. -- A separate Palworld plugin remains required; this change provides the shared deployment capability but does not invent a Palworld launcher. diff --git a/openspec/changes/support-custom-server-deployment-workflows/proposal.md b/openspec/changes/support-custom-server-deployment-workflows/proposal.md deleted file mode 100644 index 1e78c2a..0000000 --- a/openspec/changes/support-custom-server-deployment-workflows/proposal.md +++ /dev/null @@ -1,31 +0,0 @@ -## Why - -服务器创建目前要求已注册的 Run 节点,并且只投递固定生命周期动作;插件声明的端口、人数和路径字段没有进入创建请求。真实自托管场景需要先创建和编辑服务器定义,再在 Run 可用时将其部署到任意用户指定的本机目录,并支持引导式或自定义命令部署。 - -## What Changes - -- 新增可在未绑定 Run 时保存的服务器部署草稿,并允许随后绑定 Run 节点和执行部署。 -- 新增部署方式:插件引导安装、接管已有服务器、用户自定义生命周期命令。 -- 将插件声明的创建表单字段变为实际可渲染、校验和持久化的游戏配置输入;SCUM 和 Minecraft 首先使用该能力。 -- 接受用户主动输入的完整服务器根目录、工作目录和命令,但将它们作为受保护执行输入:不在普通详情、任务摘要、日志或插件桥接中回显。 -- 新增 Run 预检、部署阶段进度和可诊断的排队/领取/执行状态,替代“任务已派发”即结束的体验。 -- **BREAKING** 扩展服务器创建与生命周期任务契约,使部署定义和受保护执行输入成为显式字段,而不是复用 runtime bindings。 - -## Capabilities - -### New Capabilities - -- `server-deployment-workflows`: 草稿、Run 绑定、部署方式、受保护执行输入、预检和阶段化部署状态。 -- `plugin-create-configuration`: 插件创建字段的安全发布、渲染、校验与 SCUM/Minecraft 配置映射。 -- `run-custom-process-execution`: Run 对用户定义安装/启动/停止命令的受策略控制执行与脱敏进度回报。 - -### Modified Capabilities - -- None; the repository has no baseline OpenSpec capability specifications. - -## Impact - -- `platform/`:领域模型、DTO、验证、持久化、服务器生命周期服务、任务执行输入和 API。 -- `platform_web/`:服务器创建向导、草稿编辑、部署进度与 API types/client。 -- `plugins/`:创建 schema、SCUM/Minecraft 声明和生命周期动作模板。 -- 独立 Run 仓库:需要实现新任务执行输入、路径预检、自定义进程策略及阶段进度;本仓库不包含其源码。 diff --git a/openspec/changes/support-custom-server-deployment-workflows/specs/plugin-create-configuration/spec.md b/openspec/changes/support-custom-server-deployment-workflows/specs/plugin-create-configuration/spec.md deleted file mode 100644 index 203ea5a..0000000 --- a/openspec/changes/support-custom-server-deployment-workflows/specs/plugin-create-configuration/spec.md +++ /dev/null @@ -1,15 +0,0 @@ -## ADDED Requirements - -### Requirement: Plugin create schemas drive server creation input -The system SHALL publish validated plugin create-field schemas to the management console and SHALL render supported fields during server draft creation. Required fields, defaults, select options, numeric values, port values, and boolean values MUST be validated before saving. - -#### Scenario: Create a SCUM definition -- **WHEN** an operator chooses the SCUM plugin -- **THEN** the console renders the declared server name, game port, query port, and maximum player fields with their declared defaults - -### Requirement: Game configuration is distinct from runtime transport binding -The system SHALL persist plugin create inputs as deployment configuration and MUST NOT store game ports, player limits, paths, or startup commands in runtime binding records. - -#### Scenario: Save Minecraft port settings -- **WHEN** an operator saves Minecraft game and RCON ports in a draft -- **THEN** the values are retained as plugin create configuration and runtime binding remains reserved for declared transports diff --git a/openspec/changes/support-custom-server-deployment-workflows/specs/run-custom-process-execution/spec.md b/openspec/changes/support-custom-server-deployment-workflows/specs/run-custom-process-execution/spec.md deleted file mode 100644 index 952c38d..0000000 --- a/openspec/changes/support-custom-server-deployment-workflows/specs/run-custom-process-execution/spec.md +++ /dev/null @@ -1,22 +0,0 @@ -## ADDED Requirements - -### Requirement: Run executes protected custom lifecycle plans under declared policy -The system SHALL dispatch custom lifecycle commands only to a Run endpoint that advertises deployment-plan support and the selected execution policy. Run MUST perform local path, executable, timeout, and port preflight before executing a write or process action. - -#### Scenario: Run accepts an argv custom start plan -- **WHEN** a compatible Run claims a custom start job using argv execution mode -- **THEN** it receives the protected working directory and arguments only through the leased execution input and reports a safe preflight result - -### Requirement: Shell interpretation is explicit -The system SHALL require an explicit shell kind for a shell command string and MUST NOT infer shell interpretation from command text. The system MUST reject shell execution when the selected Run policy does not allow that shell kind. - -#### Scenario: Disallowed shell command -- **WHEN** an operator selects a shell command mode unsupported by the assigned Run -- **THEN** dispatch fails with a safe policy error and does not execute the command - -### Requirement: Run reports redacted phase progress -The Run contract SHALL report only a defined deployment phase, percent, and safe message or error code. It MUST NOT return raw host paths, raw command text, raw credentials, or direct socket values. - -#### Scenario: Preflight path failure -- **WHEN** a configured working directory is unavailable on Run -- **THEN** Run reports a `preflight` failure with a safe reason without echoing the supplied absolute path diff --git a/openspec/changes/support-custom-server-deployment-workflows/specs/server-deployment-workflows/spec.md b/openspec/changes/support-custom-server-deployment-workflows/specs/server-deployment-workflows/spec.md deleted file mode 100644 index 672ffbb..0000000 --- a/openspec/changes/support-custom-server-deployment-workflows/specs/server-deployment-workflows/spec.md +++ /dev/null @@ -1,40 +0,0 @@ -## ADDED Requirements - -### Requirement: Server definitions can be saved before Run is available -The system SHALL allow an authorized server manager to create and edit a draft server definition without a Run endpoint binding. The system MUST NOT dispatch a lifecycle job for an unbound draft. - -#### Scenario: Create an unbound draft -- **WHEN** an authorized user saves a server definition without selecting a Run endpoint -- **THEN** the system stores it in `draft` state and returns no install job - -#### Scenario: Deploy a draft after Run registration -- **WHEN** an authorized user binds a draft to a compatible online Run endpoint and requests deployment -- **THEN** the system validates the deployment definition and queues the requested lifecycle job - -### Requirement: Deployment modes support real self-hosted layouts -The system SHALL support `guided-install`, `existing-server`, and `custom-command` deployment modes. An operator MAY provide an absolute server root and working directory for all modes and lifecycle command definitions for custom-command mode. - -#### Scenario: Adopt an existing Python virtual-environment server -- **WHEN** an operator saves existing-server or custom-command mode with an absolute working directory and a Python virtual-environment startup command -- **THEN** the system stores the protected execution input and does not require the server directory to be adjacent to Run - -### Requirement: Protected execution inputs are not exposed by read APIs -The system SHALL treat supplied host paths and command text as protected execution inputs. List, detail, job, audit, log, and plugin bridge read responses MUST expose only configured state, deployment mode, and safe fingerprints or summaries. - -#### Scenario: Read a configured custom deployment -- **WHEN** an authorized user reads a server deployment definition after saving a path and command -- **THEN** the response indicates the protected fields are configured without returning their values - -### Requirement: Deployment requires an execution-capable Run only when dispatching -The system SHALL require an online compatible Run endpoint only for preflight, install, start, stop, or status dispatch. The system MUST reject dispatch when the assigned Run does not declare the versioned deployment-plan capability. - -#### Scenario: Attempt deployment with incompatible Run -- **WHEN** an operator requests deployment against a Run that lacks deployment-plan support -- **THEN** the system returns a safe compatibility reason and does not queue an executable lifecycle job - -### Requirement: Lifecycle status explains waiting and execution phases -The system SHALL surface whether a deployment job is queued, claimed, in preflight, installing, configuring, starting, or performing a health check. Safe Run failures MUST remain attached to the job and server state. - -#### Scenario: Run has not claimed deployment -- **WHEN** a deployment job remains queued -- **THEN** the server UI identifies it as waiting for Run claim rather than reporting installation progress diff --git a/openspec/changes/support-custom-server-deployment-workflows/tasks.md b/openspec/changes/support-custom-server-deployment-workflows/tasks.md deleted file mode 100644 index b786bb2..0000000 --- a/openspec/changes/support-custom-server-deployment-workflows/tasks.md +++ /dev/null @@ -1,29 +0,0 @@ -## 1. Platform deployment contracts and persistence - -- [x] 1.1 Add draft server state and protected deployment-definition domain, DTO, repository, and persistence contracts. -- [x] 1.2 Add validated create-input schemas, protected path/command updates, and safe deployment read projections. -- [x] 1.3 Add draft creation, later Run binding, and deployment dispatch services with versioned execution-plan input. - -## 2. Run-facing lifecycle and diagnostics - -- [x] 2.1 Extend lifecycle jobs with deployment-plan capability checks and redacted phase progress validation/projection. -- [x] 2.2 Document the independent Run contract for preflight, custom argv/shell policy, and safe phase reports. -- [x] 2.3 Add Platform service/API tests for unbound drafts, protected input redaction, compatibility rejection, and deployment dispatch. - -## 3. Plugin declarations - -- [x] 3.1 Extend plugin create-form declarations with supported field metadata and recommended deployment-template mappings. -- [x] 3.2 Update SCUM and Minecraft declarations with usable guided-install fields and defaults; preserve their runtime-binding semantics. -- [x] 3.3 Add manifest validation tests for the new create/deployment declaration rules. - -## 4. Management console workflows - -- [x] 4.1 Add API types/client/contracts/schemas for draft creation, deployment-definition updates, Run binding, and deploy dispatch. -- [x] 4.2 Replace the create dialog with a staged modal workflow that renders plugin fields and deployment modes, including protected full-path and custom-command inputs. -- [x] 4.3 Add server detail deployment editing and stage-aware job status without rendering protected inputs. -- [x] 4.4 Add focused frontend tests for field rendering, redaction, drafts, and queued/claimed/preflight status copy. - -## 5. Verification and delivery - -- [x] 5.1 Run focused backend, plugin, and frontend verification plus `scripts/check-structure.sh`. -- [x] 5.2 Run `openspec validate support-custom-server-deployment-workflows --strict`, mark verified tasks complete, stage only this task's files, commit, and push the current branch. diff --git a/openspec/changes/sync-implemented-docs-and-comments/.openspec.yaml b/openspec/changes/sync-implemented-docs-and-comments/.openspec.yaml deleted file mode 100644 index aee4ef1..0000000 --- a/openspec/changes/sync-implemented-docs-and-comments/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-07 diff --git a/openspec/changes/sync-implemented-docs-and-comments/design.md b/openspec/changes/sync-implemented-docs-and-comments/design.md deleted file mode 100644 index f571cae..0000000 --- a/openspec/changes/sync-implemented-docs-and-comments/design.md +++ /dev/null @@ -1,26 +0,0 @@ -## Overview - -This is a documentation synchronization change. The codebase already implements the capabilities being clarified; the work is to remove stale "gap" and "deferred" language where it contradicts the current route catalog and tests, while leaving real future-work boundaries visible. - -## Scope - -- Update stale route/protocol/frontend/plugin documentation and API comments. -- Do not alter request/response contracts, validators, services, frontend components, plugin SDK behavior, or run execution logic. -- Do not mark future features as implemented unless an implemented route/client/test already exists. - -## Target Corrections - -- Frontend API contracts should show platform and server metrics endpoints as implemented. -- AI provider protocol docs should show platform-mediated invocation as implemented while keeping live connectivity and remote model discovery deferred. -- Run artifact protocol docs should show browser download as implemented while keeping platform-to-run download, browser upload, external object storage, presigned URLs, and production throttling as future work. -- Platform API route docs should remove already-implemented config diff/file dispatch from deferred route groups. -- Handler Swagger comments for artifact/log metadata should not claim implemented chunk ingest or durable log ingest remains deferred. -- Plugin README should no longer describe marketplace, hosted plugin pages, or real lifecycle execution as future OpenSpec work when those flows now exist in bounded platform-mediated form. - -## Validation - -- Run a focused stale-marker scan for the corrected files. -- Run `scripts/check-structure.sh`. -- Run `openspec validate sync-implemented-docs-and-comments --strict`. - -No browser walkthrough is required because this change does not edit frontend pages or visual behavior. diff --git a/openspec/changes/sync-implemented-docs-and-comments/proposal.md b/openspec/changes/sync-implemented-docs-and-comments/proposal.md deleted file mode 100644 index e0b6fdf..0000000 --- a/openspec/changes/sync-implemented-docs-and-comments/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ -## Why - -Recent implementation changes completed platform metrics, config write/file dispatch, plugin bridge execution, mediated AI invocation, artifact browser download, and log/artifact transport behavior, but several route catalogs, protocol notes, frontend contracts, and handler comments still describe those capabilities as gaps or deferred work. Those stale references make it harder to tell which features are actually missing and which are already implemented. - -## What Changes - -- Update documentation and handler comments so implemented APIs are described as implemented. -- Preserve explicit future-work boundaries for live external AI connectivity, remote model discovery, external storage adapters, browser upload, platform-to-run download, production throttling, server-side log filters, package acquisition, and remote plugin hosting. -- Keep this change documentation-only; it does not add routes, runtime behavior, UI behavior, dependencies, or schema-breaking changes. - -## Capabilities - -### New Capabilities -- `implemented-documentation-sync`: Keeps implementation-facing documentation and generated API comments aligned with completed platform/run/frontend/plugin capabilities. - -### Modified Capabilities -- None. - -## Impact - -- Affected documentation and comments: - - `platform/api/routes.md` - - `platform/protocol/ai-provider-contracts.md` - - `run/protocol/artifact.md` - - `platform_web/api/contracts.md` - - `plugins/README.md` - - Swagger comments in `platform/api/resource_handlers.go` -- No API, DTO, service, repository, frontend runtime, plugin SDK, or run executor behavior changes. diff --git a/openspec/changes/sync-implemented-docs-and-comments/specs/implemented-documentation-sync/spec.md b/openspec/changes/sync-implemented-docs-and-comments/specs/implemented-documentation-sync/spec.md deleted file mode 100644 index 6273db0..0000000 --- a/openspec/changes/sync-implemented-docs-and-comments/specs/implemented-documentation-sync/spec.md +++ /dev/null @@ -1,23 +0,0 @@ -## ADDED Requirements - -### Requirement: Documentation Reflects Implemented Capabilities - -Implementation-facing documentation and API comments SHALL describe completed platform, run, frontend, and plugin capabilities as implemented when route registrations, clients, and tests already exist for those capabilities. - -#### Scenario: Previously deferred route is implemented - -- **GIVEN** a route or capability is listed in implemented route catalogs or has active client/API tests -- **WHEN** documentation or handler comments describe that same route or capability -- **THEN** they SHALL identify it as implemented instead of as a gap, placeholder, or deferred behavior. - -#### Scenario: Future work remains explicit - -- **GIVEN** a related capability is still intentionally out of scope -- **WHEN** documentation is synchronized -- **THEN** it SHALL keep that capability listed as future work without implying current implementation. - -#### Scenario: Documentation-only synchronization - -- **GIVEN** stale documentation is corrected -- **WHEN** the change is implemented -- **THEN** it SHALL NOT introduce runtime behavior, API contract, frontend page, plugin SDK, or run executor changes. diff --git a/openspec/changes/sync-implemented-docs-and-comments/tasks.md b/openspec/changes/sync-implemented-docs-and-comments/tasks.md deleted file mode 100644 index 47c186a..0000000 --- a/openspec/changes/sync-implemented-docs-and-comments/tasks.md +++ /dev/null @@ -1,16 +0,0 @@ -## 1. OpenSpec Artifacts - -- [x] 1.1 Create proposal, design, spec, and tasks artifacts for a documentation-only synchronization change. -- [x] 1.2 Validate the new change with `openspec validate sync-implemented-docs-and-comments --strict`. - -## 2. Documentation Synchronization - -- [x] 2.1 Update platform API route documentation to remove stale deferred/gap language for implemented metrics, config diff, file dispatch, log ingest/query, artifact chunks, browser artifact download, plugin bridge execution, and mediated AI invocation while preserving real future-work boundaries. -- [x] 2.2 Update AI provider, run artifact, frontend API, and plugin README documentation to match implemented behavior. -- [x] 2.3 Update stale handler Swagger comments for artifact and log stream metadata. - -## 3. Verification - -- [x] 3.1 Run focused stale-marker scans for the corrected files. -- [x] 3.2 Run `scripts/check-structure.sh`. -- [x] 3.3 Run `openspec validate sync-implemented-docs-and-comments --strict` after implementation. diff --git a/openspec/changes/synchronize-run-deployment-execution/.openspec.yaml b/openspec/changes/synchronize-run-deployment-execution/.openspec.yaml deleted file mode 100644 index 8e7013b..0000000 --- a/openspec/changes/synchronize-run-deployment-execution/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-27 diff --git a/openspec/changes/synchronize-run-deployment-execution/design.md b/openspec/changes/synchronize-run-deployment-execution/design.md deleted file mode 100644 index a139191..0000000 --- a/openspec/changes/synchronize-run-deployment-execution/design.md +++ /dev/null @@ -1,55 +0,0 @@ -## Context - -Platform stores a deployment definition on the server instance and serializes it into a leased lifecycle assignment. The currently checked-out independent Run implementation has no matching `deployment` member in its protocol, so Go silently discards the incoming JSON field. Existing Platform dispatch evidence proves only that Platform queued a snapshot. - -Raw host directories and command text are normally protected. The operator has explicitly authorized local Run startup diagnostics containing those values, but the repository's channel rules still prohibit sending them through Platform, plugins, artifacts, job results, audits, or browser logs. - -## Goals / Non-Goals - -**Goals:** - -- Execute the same typed, frozen deployment revision that Platform dispatches. -- Provide an authenticated, non-sensitive execution receipt that ties a Run result to the deployment revision. -- Load authorized saved execution values directly into the edit dialog and make clearing optional values explicit. -- Emit approved raw diagnostics only to Run's local process logger for a start operation. - -**Non-Goals:** - -- No host browsing, direct Platform-to-host access, plugin access to execution inputs, credential logging, or raw values in any remotely transmitted channel. -- No re-addition of Run source to this repository. -- No implicit conversion of arbitrary command strings into shell execution; selected shell policy remains enforced by Run. - -## Decisions - -1. **Use a typed deployment envelope shared by copied contracts, with a required protocol version and definition revision.** Platform sends the immutable definition already stored with the job; Run rejects unsupported versions or invalid action/mode combinations. This is preferred to a separate mutable lookup because it preserves the job's execution fence. - -2. **Add a compact execution receipt to the terminal result.** It contains only protocol version, deployment revision, lifecycle action, deployment mode, configured/effective-working-directory state, shell kind, and outcome. Platform validates receipt equality with the leased snapshot before projecting `Run confirmed rN`. A hash of raw values is deliberately excluded because low-entropy commands and paths are susceptible to guessing. - -3. **Keep raw diagnostics local and opt-in.** A new Run configuration flag enables startup diagnostics. When enabled, the Run process logger prints server root, effective working directory, selected shell, and the exact command immediately before launch. This writer is distinct from the process log sink and cannot be passed into Run's log batch uploader. Credentials, runtime bindings, install/stop/status commands, and environment values remain excluded. - -4. **Use explicit update operations for protected optional fields.** The deployment update DTO gains a `clearFields` list, while omitted values preserve the saved field and supplied non-empty values replace it. Shell is represented as an optional value so omission preserves it and an explicit empty shell selects direct argv mode. This prevents a normal edit from silently resetting shell configuration. - -5. **Auto-reveal only within the authorized editor route.** The editor calls the existing owner-scoped reveal endpoint when opened, stores values only in component state, signals that protected values are visible, and clears state on close. Ordinary deployment reads remain redacted. - -6. **SCUM guided installation is a typed template, not a custom command.** The first-party SCUM template is the authority for SteamCMD App `3792580`, SCUM file/config markers, Microsoft Visual C++ prerequisites, DirectX runtime, and verification. Run receives the frozen template plus the protected root and game fields, creates a controlled SteamCMD argument vector including `+force_install_dir`, and never interprets an operator or plugin supplied shell snippet for the guided path. Microsoft prerequisite artifacts use official Microsoft endpoints and silent switches under a fixed Run catalog; an internal mirror is not selected unless it is explicitly checksum-equivalent to that catalog. Run checks runtime markers before each install and verifies the marker again afterward. - -7. **Keep transport identifiers internal and present safe operation text.** `process.install` remains the Run capability because it is part of the leased job protocol. The Platform Web task card derives its title from the frozen plugin/action and deployment phase (for example, `安装 SCUM 服务端`), rather than rendering the protocol capability as user-facing copy. - -## Risks / Trade-offs - -- [Raw local diagnostics can be copied from the host console] → Diagnostics are opt-in, local-only, action-scoped, and clearly warn operators; no credentials or environment are included. -- [Mixed Platform/Run releases can lose execution confirmation] → Version/capability negotiation rejects deployment-aware jobs until the Run supports the protocol, rather than silently ignoring input. -- [An editor request can reveal operational values to an authorized browser] → It remains owner-authorized, uses no shared cache, and clears on dialog close. -- [Existing clients cannot express clearing] → The additive `clearFields` field preserves existing requests while enabling explicit removal. -- [A download mirror may be stale or tampered with] → Prefer the official Microsoft catalog and a fixed checksum/signature policy. Mirrors are acceleration only after equivalence verification; they are never the trust root. - -## Migration Plan - -1. Release Run support and advertise a new deployment-execution capability. -2. Release Platform support, requiring that capability before it dispatches a typed deployment job. -3. Enable local diagnostics only on Runs where the operator sets the explicit configuration flag. -4. Roll back by disabling the flag and stopping Platform dispatch to the new capability; saved deployment definitions remain intact. - -## Open Questions - -- None. The raw diagnostic allowance is constrained to the local Run process logger by the operator's instruction. diff --git a/openspec/changes/synchronize-run-deployment-execution/proposal.md b/openspec/changes/synchronize-run-deployment-execution/proposal.md deleted file mode 100644 index 9479771..0000000 --- a/openspec/changes/synchronize-run-deployment-execution/proposal.md +++ /dev/null @@ -1,34 +0,0 @@ -## Why - -Platform persists a complete deployment definition, but the independent Run worker currently ignores the deployment body carried in its job assignment. Operators can inspect protected fields only through an extra action, and there is no trustworthy confirmation that a start job used the saved revision. - -This change makes the editor load the authorized saved definition directly and makes Platform and Run execute and acknowledge the same frozen deployment revision. The user has explicitly approved printing the raw server directory and execution command in Run's local startup diagnostics only. - -## What Changes - -- Add a typed, versioned deployment execution body and execution receipt to the Platform–Run lifecycle contract. **BREAKING:** compatible Run binaries must be upgraded before Platform dispatches deployment-aware lifecycle jobs. -- Make Run validate and execute the frozen deployment body for install, start, stop, and status actions, including the configured working-directory and shell policy. -- Add opt-in local-only Run startup diagnostics that print the raw server directory, effective working directory, shell, and executable command. These diagnostics must not be uploaded, returned through the job API, stored as artifacts, or exposed to plugins or Platform Web. -- Return a non-sensitive receipt containing the deployment revision and execution mode so Platform can distinguish dispatch evidence from Run execution confirmation. -- Automatically fetch and prefill the authorized saved deployment inputs when opening the edit workflow, then clear them when the dialog closes. -- Replace ambiguous empty-string update handling with explicit preserve/set/clear semantics and prevent an omitted shell from resetting the saved shell. -- Make first-party SCUM guided installation fully plugin-owned: Run must consume the frozen SCUM template, ensure SteamCMD plus declared Microsoft prerequisites, install App `3792580` into the selected root, materialize configuration, and verify the resulting server. Operators supply the target directory and game settings, never lifecycle commands or prerequisite installers. -- Replace raw lifecycle capability text such as `process.install` in operator task surfaces with the plugin-aware operation name and phase, while retaining the capability as the internal routing identifier. - -## Capabilities - -### New Capabilities - -- `run-deployment-execution-receipts`: Run consumes a frozen deployment definition and confirms the revision it executed without exposing protected values remotely. -- `local-run-startup-diagnostics`: opt-in local Run diagnostics print approved raw execution details while remaining outside all Platform channels. - -### Modified Capabilities - -- None. - -## Impact - -- Platform job-channel DTOs, domain contracts, validation, lifecycle dispatch/result projection, and deployment editor API/client/view code. -- Independent `run.git` protocol, lifecycle executor, worker diagnostics, tests, and release compatibility metadata. No Run source is added to this repository. -- Operator workflow: opening an authorized stopped-server editor reads protected deployment values; normal list/detail/job/log/audit responses remain redacted. -- SCUM guided deployment: first-party manifest declaration, Platform's frozen assignment, and the independent Run executor. diff --git a/openspec/changes/synchronize-run-deployment-execution/specs/local-run-startup-diagnostics/spec.md b/openspec/changes/synchronize-run-deployment-execution/specs/local-run-startup-diagnostics/spec.md deleted file mode 100644 index 65dcd99..0000000 --- a/openspec/changes/synchronize-run-deployment-execution/specs/local-run-startup-diagnostics/spec.md +++ /dev/null @@ -1,19 +0,0 @@ -## ADDED Requirements - -### Requirement: Local raw startup diagnostics -When explicitly enabled in Run configuration, Run SHALL print the raw server root, effective working directory, shell, and exact start command to its local process logger immediately before executing a valid start job. - -#### Scenario: Enabled diagnostics print local start context -- **WHEN** an enabled Run begins a valid custom-command start job -- **THEN** its local process logger SHALL contain the configured root, effective directory, shell, and command - -#### Scenario: Disabled diagnostics remain silent -- **WHEN** local startup diagnostics are not enabled -- **THEN** Run SHALL not print raw deployment values - -### Requirement: Raw diagnostics remain local -Run SHALL NOT include raw deployment values in log-batch uploads, job progress, terminal results, artifacts, audits, or plugin-facing payloads. - -#### Scenario: Local diagnostics do not enter remote channels -- **WHEN** enabled Run diagnostics are emitted during a start job -- **THEN** the corresponding uploaded log events and terminal result SHALL contain no raw directory or command text diff --git a/openspec/changes/synchronize-run-deployment-execution/specs/run-deployment-execution-receipts/spec.md b/openspec/changes/synchronize-run-deployment-execution/specs/run-deployment-execution-receipts/spec.md deleted file mode 100644 index ce7c5d8..0000000 --- a/openspec/changes/synchronize-run-deployment-execution/specs/run-deployment-execution-receipts/spec.md +++ /dev/null @@ -1,49 +0,0 @@ -## ADDED Requirements - -### Requirement: Frozen deployment execution -Platform SHALL send a typed, versioned deployment definition with every lifecycle job for a configured server, and Run SHALL reject a definition it cannot validate or execute for the requested lifecycle action. - -#### Scenario: Start uses the leased revision -- **WHEN** Run receives a valid `process.start` job with deployment revision 7 -- **THEN** it SHALL execute only the fields in that leased revision and SHALL not fetch a mutable replacement definition - -#### Scenario: Incompatible Run is rejected -- **WHEN** a selected Run does not advertise deployment-execution support -- **THEN** Platform SHALL reject dispatch before assigning a lifecycle job - -### Requirement: Execution revision receipt -Run SHALL include a non-sensitive deployment execution receipt in each terminal deployment-aware lifecycle result, and Platform SHALL validate the receipt against the job's leased definition before projecting confirmation. - -#### Scenario: Matching receipt is projected -- **WHEN** Run succeeds a start job and returns the job's deployment revision and action -- **THEN** Platform SHALL show that Run confirmed execution of that revision - -#### Scenario: Mismatched receipt is rejected -- **WHEN** Run reports a deployment revision or lifecycle action different from the lease -- **THEN** Platform SHALL reject the terminal result and SHALL not mark the job successful - -### Requirement: Explicit deployment field clearing -Platform SHALL preserve a deployment value when an update omits it and SHALL clear an optional field only when the update explicitly names that field for clearing. - -#### Scenario: Omitted shell remains unchanged -- **WHEN** an editor saves a deployment update without a shell value -- **THEN** Platform SHALL preserve the saved shell selection - -#### Scenario: Optional command is cleared -- **WHEN** an editor submits `stopCommand` in its explicit clear list -- **THEN** Platform SHALL store no stop command for the next deployment revision - -### Requirement: SCUM guided deployment is template-owned -For a frozen SCUM guided-install assignment, Run SHALL execute the declared SCUM installation template rather than requiring an operator-provided install or start command. The operator supplies only the protected target root and plugin create inputs. - -#### Scenario: Missing runtime prerequisites are installed silently -- **WHEN** a Windows SCUM guided-install detects a required declared Microsoft runtime is absent -- **THEN** Run SHALL obtain the approved Microsoft artifact, invoke only its fixed silent installer arguments, verify the runtime marker, and continue only when verification passes - -#### Scenario: SteamCMD installs into the selected root -- **WHEN** a valid SCUM guided-install is executed for root `C:\\scumserver` -- **THEN** Run SHALL invoke SteamCMD with `+force_install_dir C:\\scumserver`, anonymous login, App `3792580`, validation, and quit as separate argument values - -#### Scenario: Guided execution does not disclose raw values remotely -- **WHEN** Run completes or fails a SCUM guided-install -- **THEN** its progress, result, evidence, artifact names, and uploaded logs SHALL contain no raw root, command line, credential, or installer URL diff --git a/openspec/changes/synchronize-run-deployment-execution/tasks.md b/openspec/changes/synchronize-run-deployment-execution/tasks.md deleted file mode 100644 index cb8296d..0000000 --- a/openspec/changes/synchronize-run-deployment-execution/tasks.md +++ /dev/null @@ -1,32 +0,0 @@ -## 1. Task boundaries and contracts - -- [x] 1.1 正向提示词:让授权操作者在编辑已停止服务器时读取完整已保存部署配置;让 Run 执行并确认同一冻结修订;开启本机诊断后可直接打印目录和启动命令,且所有成功路径都有自动化验证。 -- [x] 1.2 方向提示词:在 `platform/` 与 `platform_web/` 沿用现有部署 DTO/生命周期/编辑工作流模式;在独立 `run.git` 的协议和生命周期执行器中实现匹配契约;验证 `go test ./...`、前端测试/类型检查、`scripts/check-structure.sh` 与严格 OpenSpec 校验。 -- [x] 1.3 任务边界:不把 Run 源码移入 browser 仓库;不输出凭据、运行绑定或环境变量;不让原始目录/命令进入 Platform API、日志回传、任务结果、工件、审计或插件接口;不增加计费、云主机或无关 SaaS 能力。 - -## 2. Platform deployment contract - -- [x] 2.1 Add typed deployment execution version/receipt contracts and validation for frozen lifecycle jobs. -- [x] 2.2 Require compatible Run capability before lifecycle dispatch and project validated Run confirmation in deployment views. -- [x] 2.3 Add explicit deployment-field clearing and preserve omitted shell values; cover service/API behavior with tests. -- [x] 2.4 Freeze SCUM prerequisite/template execution data into the leased deployment plan and validate it as a first-party Windows-only guided installation contract. - -## 3. Platform editor workflow - -- [x] 3.1 Automatically retrieve authorized saved inputs when the deployment editor opens and clear them on close. -- [x] 3.2 Display dispatch revision separately from Run-confirmed revision and update frontend tests/types. - -## 4. Independent Run execution - -- [x] 4.1 Extend independent `run.git` protocol and validation with the typed deployment envelope and safe execution receipt. -- [x] 4.2 Execute deployment-aware lifecycle actions using the frozen root, working directory, shell, and commands. -- [x] 4.3 Add opt-in local-only raw startup diagnostics that bypass the upload log sink and exclude credentials/environment values. -- [x] 4.4 Add Run protocol/runtime tests covering execution, receipt fences, enabled diagnostics, and remote-channel redaction. -- [x] 4.5 Implement the SCUM guided executor: dependency probes and silent Microsoft prerequisite installation, SteamCMD bootstrap, `+force_install_dir` installation, configuration materialization, verification evidence, and local-only diagnostic output. - -## 5. Verification and delivery - -- [x] 5.1 Run browser Platform/backend/frontend validation and strict OpenSpec validation. -- [x] 5.2 Run independent Run test suite and inspect both worktrees for scoped changes only. -- [x] 5.3 Stage, commit, and push scoped changes on `main` in both repositories. -- [x] 5.4 Replace raw lifecycle capability labels in the server-management task views and run focused UI tests. diff --git a/openspec/changes/verify-current-platform-e2e-baseline/.openspec.yaml b/openspec/changes/verify-current-platform-e2e-baseline/.openspec.yaml deleted file mode 100644 index aee4ef1..0000000 --- a/openspec/changes/verify-current-platform-e2e-baseline/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-07-07 diff --git a/openspec/changes/verify-current-platform-e2e-baseline/design.md b/openspec/changes/verify-current-platform-e2e-baseline/design.md deleted file mode 100644 index c4788fa..0000000 --- a/openspec/changes/verify-current-platform-e2e-baseline/design.md +++ /dev/null @@ -1,85 +0,0 @@ -## Context - -The architecture queue now needs a proof-oriented checkpoint. Earlier changes added platform APIs, durable storage, run control/job/log/artifact channels, platform_web pages, plugin registry/bridge contracts, and AI-provider management, but individual completion evidence does not answer whether the current integrated platform is operational or still relying on local fallback/demo behavior. - -This change is intentionally verification-only. It should run the current system, inspect the exposed behavior, and produce a clear proof report that classifies each required flow as: - -- `real`: backed by platform/run/plugin behavior with executable evidence and no demo fallback needed. -- `partial`: some real behavior exists, but a documented missing piece prevents full operational proof. -- `demo-only`: the visible behavior is static seed/local fallback data or simulated behavior that cannot prove real platform operation. -- `blocked`: verification cannot run because of a reproducible environment, tooling, credential, or dependency blocker. - -## Goals / Non-Goals - -**Goals:** - -- Verify the required first-party frontend areas: 首页、服务器管理、插件市场、用户管理、AI 提供商管理. -- Verify platform APIs are backed by current storage and service behavior rather than only frontend seed data. -- Verify run-mediated lifecycle behavior for create/install, start, stop, job ack/result, and state projection. -- Verify durable log history survives through the current log ingest/query path and remains independent from artifact/file transfer work. -- Verify artifact download/upload and file dispatch use platform-mediated contracts with bounded transfer evidence. -- Verify game management plugin manifests, SDK bridge requests, AI requests, and file/config/log/run capabilities do not bypass platform authorization. -- Produce a proof report with command output references, browser walkthrough notes, and gap classifications. - -**Non-Goals:** - -- No implementation of missing product behavior. -- No redesign of platform_web pages or visual style. -- No new game plugin lifecycle implementation beyond verifying current behavior. -- No billing, cloud host sales, agent-provider/cloud-provider workflows, unrelated SaaS marketplace features, or provider marketplace behavior. -- No direct plugin-to-run, browser-to-run, raw host path, raw socket, raw credential, or raw AI-key exposure. - -## Decisions - -### Decision 1: Verification produces an explicit proof report - -The implementation should create or update a baseline proof report inside the change that lists every required flow, its classification, the evidence command or browser step, and any follow-up OpenSpec recommendation. - -Alternative considered: rely only on test pass/fail output. Rejected because a passing unit suite does not show whether user-visible workflows are real, partial, demo-only, or blocked. - -### Decision 2: Browser walkthrough is required for visible first-party areas - -The proof must open the frontend in a browser and walk through 首页、服务器管理、插件市场、用户管理、AI 提供商管理 plus the plugin/server detail surfaces needed to prove lifecycle, logs, artifacts, config, AI, and bridge behavior. If the walkthrough cannot run, the report must classify the affected flows as blocked with exact commands and errors. - -Alternative considered: use server-side rendered tests only. Rejected because the queue requires browser acceptance for frontend-facing proof. - -### Decision 3: Real-vs-demo classification is based on backing behavior - -Frontend pages that render local seed data, local fallback state, or simulated completion without platform/run evidence should be classified as `demo-only` or `partial`, even if they look complete. Real classification requires platform API responses, run/job/log/artifact evidence, and plugin boundary checks where applicable. - -Alternative considered: classify by UI completeness. Rejected because the user specifically needs to know whether functionality is real or demo-only. - -### Decision 4: Channel isolation is verified through concurrent or adjacent operations - -The log/artifact/file proof should include evidence that durable log ingest/control/job behavior remains independent from artifact or file operations. The baseline may use existing tests or a local smoke script if they demonstrate the isolation requirement without adding product behavior. - -Alternative considered: document channel isolation from architecture alone. Rejected because this change is about current executable reality. - -### Decision 5: Follow-up gaps become backlog recommendations, not fixes - -When a flow is partial, demo-only, or blocked, the report should identify the smallest follow-up OpenSpec needed to make it real. This change stops at proof and recommendations unless the user explicitly asks to implement a follow-up. - -Alternative considered: fix discovered gaps immediately. Rejected because the prompt requires stopping after this OpenSpec is ready and the implementation scope should remain verification-only. - -## Risks / Trade-offs - -- [Risk] Environment blockers can hide real behavior. Mitigation: record exact blocker commands and classify only affected flows as blocked. -- [Risk] Existing local fallback data may make pages appear operational. Mitigation: require API/run evidence before assigning `real`. -- [Risk] End-to-end setup may be slower than unit tests. Mitigation: tasks define a repeatable command sequence and allow narrower proof scripts when they cover the same contracts. -- [Risk] Verification may discover many gaps. Mitigation: prioritize follow-up recommendations by required first-party area and channel boundary risk. - -## Migration Plan - -1. Add the proof report structure and any small verification scripts or fixtures required to run the baseline. -2. Run platform, run, plugin, and frontend verification commands. -3. Start the local stack needed for platform_web browser walkthrough and platform/run integration checks. -4. Walk through required frontend areas and record whether data/actions are API-backed, run-backed, local fallback, or blocked. -5. Classify every required flow and list follow-up OpenSpec recommendations for non-real flows. - -Rollback is simple before implementation closes: remove the proof report and any verification-only scripts added by this change. - -## Open Questions - -- Whether the implementation should use docker-compose services or in-process test servers as the primary local stack for proof. -- Whether the final proof report should live only under this change or be promoted into persistent project documentation after acceptance. -- Whether blocked browser tooling should be resolved by in-app browser automation, Chrome automation, or a project-owned Playwright acceptance suite. diff --git a/openspec/changes/verify-current-platform-e2e-baseline/proof-report.md b/openspec/changes/verify-current-platform-e2e-baseline/proof-report.md deleted file mode 100644 index 06dfdb2..0000000 --- a/openspec/changes/verify-current-platform-e2e-baseline/proof-report.md +++ /dev/null @@ -1,97 +0,0 @@ -# Current Platform E2E Baseline Proof Report - -Date: 2026-07-08 - -## Classification Legend - -- `real`: backed by executable platform/run/plugin evidence in this baseline. -- `partial`: some real backing exists, but a missing integration step prevents full end-to-end proof. -- `demo-only`: visible behavior is local fallback, seed data, or simulated behavior. -- `blocked`: verification could not run because of a reproducible environment, service, auth, or tooling blocker. - -## Command Evidence - -| Area | Command | Result | Evidence | -| --- | --- | --- | --- | -| Platform | `cd platform && go test ./... -count=1` | Passed | Packages `api`, `config`, `domain`, `dto`, `model`, `repo`, `service`, and `validator` passed. | -| Run | `cd run && go test ./... -count=1` | Passed | Packages `api`, `config`, `protocol`, `runtime`, and `spool` passed. | -| Plugins | `cd plugins && npm run typecheck` | Passed | TypeScript completed with no errors. | -| Plugins | `cd plugins && npm run test` | Passed | Vitest reported 1 test file / 10 tests passed. | -| Plugins | `cd plugins && npm run validate:manifest` | Passed after sandbox retry | Initial sandbox run failed with `listen EPERM` for the `tsx` IPC pipe; escalated run printed `validated examples/dev-game-plugin/manifest.json`. | -| Frontend | `cd platform_web && npm run typecheck` | Passed | TypeScript completed with no errors. | -| Frontend | `cd platform_web && npm test` | Passed | Vitest reported 11 test files / 47 tests passed. | -| Frontend | `cd platform_web && npm run build` | Passed | Vite built `dist/` assets successfully. | -| Browser server | `cd platform_web && VITE_ENABLE_LOCAL_AUTH_FALLBACK=true npm run dev -- --port 5173` | Passed after sandbox retry | Initial sandbox run failed with `listen EPERM 127.0.0.1:5173`; escalated run served `http://127.0.0.1:5174/`. | - -## Browser Walkthrough Evidence - -The browser walkthrough used `http://127.0.0.1:5174/` with `VITE_ENABLE_LOCAL_AUTH_FALLBACK=true`. - -| Page / Flow | Browser Result | Classification | Follow-up | -| --- | --- | --- | --- | -| Auth entry | Login screen showed `本地回退可用` and `进入本地回退工作台`, proving the auth API was unavailable in this local browser stack. | `partial` | Use a local stack that starts platform API, run, and frontend together so browser auth can prove real sessions. | -| 服务器管理 | Local fallback entered `#/servers` as `Local Server Operator`; page showed `服务器列表加载失败` with `path /api/v1/jobs was not found`. | `partial` | Fix local browser stack/API routing and rerun server lifecycle walkthrough against real platform APIs. | -| 首页 | Navigating to `#/home` as fallback server admin redirected/rendered the server workspace rather than 首页. | `blocked` | Verify with a real platform-admin session. | -| 插件市场 | Navigating to `#/plugins` as fallback server admin redirected/rendered the server workspace. | `blocked` | Verify with a real platform-admin session and API-backed plugin marketplace data. | -| 用户管理 | Navigating to `#/users` as fallback server admin redirected/rendered the server workspace. | `blocked` | Verify with a real platform-admin session. | -| AI 提供商管理 | Navigating to `#/aiProviders` as fallback server admin redirected/rendered the server workspace. | `blocked` | Verify with a real platform-admin session and API-backed provider data. | -| Personal/account settings | `#/profile` rendered `个人设置`, `本地会话`, editable profile fields, palette/background controls, and logout. | `demo-only` | Rerun with a real API-authenticated user to prove profile/theme persistence. | -| Visible unsafe fields | Browser-visible fallback pages did not expose raw host paths, run credentials, direct sockets, raw AI keys, or plugin-owned transport details. | `partial` | Repeat on real server/plugin/detail pages after API-backed browser stack works. | - -## Platform API and Storage Matrix - -| Flow | Classification | Evidence | Follow-up | -| --- | --- | --- | --- | -| Auth/session API | `real` for handler/service tests; `partial` for browser | `platform` test suite passed; browser auth used local fallback because API was unavailable. | Add a repeatable local full-stack browser auth smoke path. | -| Users API | `real` for platform tests; `blocked` in browser | `platform` test suite passed; fallback role cannot open 用户管理. | Browser verify with real platform-admin session. | -| Server instances and lifecycle APIs | `real` for platform tests; `partial` in browser | `platform` tests include server lifecycle/API packages; browser server page failed `/api/v1/jobs`. | Fix local stack/proxy and rerun create/start/stop browser flow. | -| Plugin registry and marketplace APIs | `real` for platform/plugin tests; `blocked` in browser | `platform` and `plugins` tests passed; fallback role could not open 插件市场. | Browser verify marketplace with API-backed admin session. | -| AI provider APIs | `real` for platform tests; `blocked` in browser | `platform` validator/dto/API tests passed and protect raw keys; fallback role could not open AI 提供商管理. | Browser verify create/test/status with API-backed admin session. | -| Jobs API | `real` for platform/run tests; `partial` in browser | `platform` and `run` tests passed; browser surfaced `path /api/v1/jobs was not found`. | Start compatible platform API with frontend proxy for browser proof. | -| Logs API/storage | `partial` | `platform` and `run` tests passed for log-related packages, but this baseline did not prove restart-surviving log history in a live stack. | Add local durable log history smoke with restart/query evidence. | -| Artifacts API/storage | `real` for chunk/checksum/download tests; `partial` for live stack | `platform` and `run` tests passed; artifact download tests prove platform-mediated references and safe fields. No live browser artifact download was reachable. | Rerun browser server detail artifact flow with real API stack. | -| Config diff/write and file dispatch | `real` for platform tests; `partial` for browser | `platform` tests passed; browser could not reach server detail/config due server list API failure. | Rerun config diff/write dispatch in browser with real server instance. | -| Storage durability | `partial` | Package tests passed, but this baseline did not run a database restart or cross-process durability smoke. | Add explicit MySQL-backed create/restart/query proof in local debug workspace. | - -## Run Channel Matrix - -| Flow | Classification | Evidence | Follow-up | -| --- | --- | --- | --- | -| Control hello/heartbeat | `real` at package level | `cd run && go test ./... -count=1` passed `api` and `protocol` packages. | Include a live run-to-platform heartbeat in local debug workspace. | -| Job claim/ack/progress/result | `real` at package level | `run` package tests passed for API/runtime behavior. | Add integrated platform/run smoke with actual queued job. | -| Lifecycle install/start/stop executor | `real` at package level; `partial` end to end | `run` runtime tests passed; browser could not prove create/start/stop because platform API stack was unavailable. | Implement/verify real game plugin lifecycle proof. | -| Log spool/ingest acknowledgement | `real` at package level; `partial` for durable history | `run` spool tests passed; no live restart/query proof ran. | Add durable log history smoke. | -| Artifact chunk/resume/checksum | `real` at package level | `run` artifact API tests passed and platform artifact tests passed. | Add browser artifact download walkthrough against a real completed artifact. | -| Channel isolation | `partial` | Prior package-level tests passed, but this baseline did not run concurrent live artifact/file transfer alongside heartbeat/job/log traffic. | Implement hardening proof for log/artifact channel isolation. | - -## Plugin Boundary Matrix - -| Flow | Classification | Evidence | Follow-up | -| --- | --- | --- | --- | -| Manifest schema validation | `real` | `npm run validate:manifest` passed for `examples/dev-game-plugin/manifest.json` after sandbox retry. | -| Unsafe manifest rejection | `real` | `npm run test` passed; tests include rejection of direct run and raw AI key requests. | -| SDK bridge request envelopes | `real` | `npm run typecheck` and `npm run test` passed; tests cover typed bridge request envelopes without owning transport. | -| AI invocation request boundaries | `real` at SDK/manifest level | Tests and schema include `ai.invoke`; plugin docs state provider keys remain platform-mediated. | -| Real multi-instance game server operation | `partial` | Plugin manifests and SDK can request lifecycle/log/artifact/AI capabilities, but this baseline did not prove a real plugin creating/managing multiple live server instances through platform/run. | Generate/implement `implement-real-game-plugin-lifecycle-proof`. | - -## Required First-Party Area Summary - -| Area | Current Baseline Classification | Reason | -| --- | --- | --- | -| 首页 | `blocked` | Fallback server-admin browser session redirected/rendered server workspace; no platform-admin browser proof. | -| 服务器管理 | `partial` | API/package evidence exists, but browser flow failed `/api/v1/jobs` without a live platform API stack. | -| 插件市场 | `blocked` | Fallback browser session could not access platform-admin route; package/API tests pass. | -| 用户管理 | `blocked` | Fallback browser session could not access platform-admin route; package/API tests pass. | -| AI 提供商管理 | `blocked` | Fallback browser session could not access platform-admin route; package/API tests pass. | - -## Follow-up OpenSpec Recommendations - -1. `implement-real-game-plugin-lifecycle-proof`: prove one game management plugin can create and manage multiple server instances only through platform-mediated platform/run contracts. -2. `implement-local-debug-workspace`: provide one repeatable command path that starts platform, run, frontend, storage, and plugin fixtures for browser/API proof without relying on local fallback. -3. `implement-browser-acceptance-suite`: automate browser coverage for 首页、服务器管理、插件市场、用户管理、AI 提供商管理 and plugin/server detail operations. -4. `harden-log-artifact-channel-isolation`: run concurrent artifact/file operations while proving control heartbeat, job ack/result, and durable log ingest continue independently. -5. Reopen/finish `fix-env-profile-settings` task `3.5` in a working browser/dev-server session to close the older guard. - -## Bottom Line - -The codebase has substantial real backend/run/plugin capability evidence from tests, but the current local browser proof is not yet a real platform-wide E2E baseline. The visible browser experience fell back to a local server-admin session, server management failed on `/api/v1/jobs`, and platform-admin first-party areas could not be reached. The next architecture work should make the real full-stack proof path repeatable, then prove a game plugin lifecycle against it. diff --git a/openspec/changes/verify-current-platform-e2e-baseline/proposal.md b/openspec/changes/verify-current-platform-e2e-baseline/proposal.md deleted file mode 100644 index 730bb4c..0000000 --- a/openspec/changes/verify-current-platform-e2e-baseline/proposal.md +++ /dev/null @@ -1,27 +0,0 @@ -## Why - -The architecture stream has implemented many platform, run, frontend, and plugin capabilities, but the current project still needs an evidence-driven baseline that proves which first-party flows are real, partial, demo-only, or blocked. The next change should verify the existing product surface before adding more product scope, so follow-up work can target gaps instead of assuming the platform is operational end to end. - -## What Changes - -- Add a baseline verification change that audits current functionality across `platform/`, `run/`, `platform_web/`, and `plugins/`. -- Classify required first-party areas and system flows as `real`, `partial`, `demo-only`, or `blocked` using executable evidence. -- Require API/run/plugin proof commands, frontend build/test commands, structure validation, and browser walkthrough evidence. -- Require a proof report that covers 首页、服务器管理、插件市场、用户管理、AI 提供商管理, run-mediated lifecycle, durable logs, artifact/file transfer, and plugin capability boundaries. -- Keep this change verification-only: it records current reality and follow-up gaps, but does not implement product fixes. - -## Capabilities - -### New Capabilities - -- `current-platform-e2e-baseline`: Evidence-based classification of current platform behavior across backend APIs, run executor channels, frontend workflows, and game management plugin boundaries. - -### Modified Capabilities - -- None. - -## Impact - -- Affects OpenSpec verification artifacts and may add proof scripts, reports, or test harnesses under the matching roots. -- Does not add billing, cloud host sales, agent-provider/cloud-provider workflows, unrelated marketplace features, direct browser/plugin access to run, raw host path exposure, or raw AI key exposure. -- Produces follow-up implementation recommendations only after evidence exists. diff --git a/openspec/changes/verify-current-platform-e2e-baseline/specs/current-platform-e2e-baseline/spec.md b/openspec/changes/verify-current-platform-e2e-baseline/specs/current-platform-e2e-baseline/spec.md deleted file mode 100644 index 0e6154c..0000000 --- a/openspec/changes/verify-current-platform-e2e-baseline/specs/current-platform-e2e-baseline/spec.md +++ /dev/null @@ -1,64 +0,0 @@ -## ADDED Requirements - -### Requirement: Baseline proof report classifies current behavior -The repository SHALL provide an evidence-based baseline proof report that classifies current functionality across platform, run, platform_web, and plugins as `real`, `partial`, `demo-only`, or `blocked`. - -#### Scenario: Required flow is classified -- **WHEN** the baseline verification runs for a required first-party or channel flow -- **THEN** the report MUST record the classification, evidence source, command or browser step, and follow-up recommendation when the classification is not `real` - -#### Scenario: Demo fallback is visible -- **WHEN** a frontend or plugin flow renders local seed data, local fallback data, or simulated completion without platform/run evidence -- **THEN** the report MUST classify that flow as `demo-only` or `partial` instead of `real` - -#### Scenario: Verification is blocked -- **WHEN** a flow cannot be verified because a local service, browser, dependency, credential, or environment condition fails -- **THEN** the report MUST classify the flow as `blocked` and include the exact command or browser action and error that prevented verification - -### Requirement: First-party frontend areas are browser verified -The baseline SHALL include a browser walkthrough for 首页、服务器管理、插件市场、用户管理、AI 提供商管理 and the personal/account navigation needed to identify authenticated workspace behavior. - -#### Scenario: Browser walkthrough covers first-party pages -- **WHEN** the browser walkthrough is executed -- **THEN** it MUST visit every required first-party area and record whether each page is backed by platform APIs, local fallback data, or inaccessible state - -#### Scenario: Browser walkthrough checks operational actions -- **WHEN** the walkthrough reaches server, plugin, user, AI provider, config, log, artifact, or plugin bridge actions -- **THEN** it MUST record whether the action uses platform-mediated APIs and whether unsafe raw host paths, run credentials, direct sockets, or raw AI keys are absent from visible UI state - -### Requirement: Platform API and storage baseline is verified -The baseline SHALL verify that platform API behavior for authentication/session, server instances, users, plugins, AI providers, jobs, logs, artifacts, and file/config dispatch is backed by the current service and storage implementation. - -#### Scenario: Platform API command evidence exists -- **WHEN** platform baseline verification runs -- **THEN** it MUST execute platform tests or smoke commands that cover the required API areas and record pass/fail evidence in the proof report - -#### Scenario: Storage behavior is classified -- **WHEN** platform data is created, updated, queried, or restarted in the baseline -- **THEN** the report MUST classify whether that data is durable, in-memory only, seed data, or blocked from verification - -### Requirement: Run-mediated lifecycle and channel behavior is verified -The baseline SHALL verify current run-mediated server lifecycle, job, log ingest, artifact transfer, and file dispatch behavior without exposing run internals to browsers or plugins. - -#### Scenario: Lifecycle path is proven -- **WHEN** the baseline verifies server lifecycle behavior -- **THEN** it MUST exercise or cite executable evidence for create/install, start, stop, job claim/ack/result, and server state projection through platform-mediated contracts - -#### Scenario: Log history is proven -- **WHEN** the baseline verifies log ingest -- **THEN** it MUST prove whether log history is durable and queryable after batch acknowledgement or classify the missing durability as `partial`, `demo-only`, or `blocked` - -#### Scenario: Artifact and file channels are proven -- **WHEN** the baseline verifies artifact transfer and file/config dispatch -- **THEN** it MUST prove bounded chunk/checksum or dispatch behavior and record whether these operations remain separate from control heartbeat, job ack/result, and log ingest - -### Requirement: Plugin capability boundaries are verified -The baseline SHALL verify game management plugin manifests, SDK bridge requests, plugin page execution, AI invocation requests, and file/config/log/run capabilities stay platform-mediated. - -#### Scenario: Plugin boundary proof exists -- **WHEN** plugin baseline verification runs -- **THEN** it MUST execute plugin validation/type/test commands and record evidence that plugin code does not include platform auth storage, raw AI keys, direct run sockets, raw host paths, or direct run transport - -#### Scenario: Plugin flow cannot operate real server behavior -- **WHEN** a plugin can render or request a capability but cannot complete a real platform/run-backed operation -- **THEN** the report MUST classify that plugin flow as `partial` or `demo-only` and recommend the follow-up OpenSpec needed to make it real diff --git a/openspec/changes/verify-current-platform-e2e-baseline/tasks.md b/openspec/changes/verify-current-platform-e2e-baseline/tasks.md deleted file mode 100644 index 579d364..0000000 --- a/openspec/changes/verify-current-platform-e2e-baseline/tasks.md +++ /dev/null @@ -1,59 +0,0 @@ -## 1. Baseline Report Structure - -- [x] 1.1 Add a proof report under `openspec/changes/verify-current-platform-e2e-baseline/` that lists every required flow, classification (`real`, `partial`, `demo-only`, `blocked`), evidence, and follow-up recommendation. -- [x] 1.2 Define the required flow matrix for platform APIs, run channels, platform_web pages, and plugin capability boundaries. -- [x] 1.3 Ensure non-real classifications identify the smallest follow-up OpenSpec needed to close the gap. - -## 2. Platform and Storage Verification - -- [x] 2.1 Run `cd platform && go test ./... -count=1` and record evidence. -- [x] 2.2 Run platform API/storage smoke coverage for auth/session, users, server instances, plugin marketplace, AI providers, jobs, logs, artifacts, config diff/write, and file dispatch; record exact command(s) used. -- [x] 2.3 Classify each platform API area as real, partial, demo-only, or blocked, including whether data is durable, in-memory, seed-only, or unavailable. - -## 3. Run and Channel Verification - -- [x] 3.1 Run `cd run && go test ./... -count=1` and record evidence. -- [x] 3.2 Verify run control hello/heartbeat, job claim/ack/progress/result, lifecycle install/start/stop execution, log spool/ingest acknowledgement, and artifact chunk/resume/checksum behavior; record exact command(s) used. -- [x] 3.3 Verify or classify whether artifact/file transfer remains independent from control heartbeat, job ack/result, and durable log ingest. - -## 4. Plugin Verification - -- [x] 4.1 Run `cd plugins && npm run typecheck && npm run test && npm run validate:manifest` and record evidence. -- [x] 4.2 Verify plugin manifests, SDK bridge requests, plugin page execution requests, AI invocation requests, and file/config/log/run capability payloads remain platform-mediated. -- [x] 4.3 Classify whether current game management plugins can perform real multi-instance server operations or only render/request demo or partial behavior. - -## 5. Frontend and Browser Walkthrough - -- [x] 5.1 Run `cd platform_web && npm run typecheck && npm test && npm run build` and record evidence. -- [x] 5.2 Start the required local stack for browser verification. Record the exact command(s), such as `cd platform_web && VITE_ENABLE_LOCAL_AUTH_FALLBACK=true npm run dev -- --port 5173`, plus any platform/run service commands needed for API-backed proof. -- [x] 5.3 In a browser, walk through 首页、服务器管理、插件市场、用户管理、AI 提供商管理, and personal/account navigation. Record for each page whether it is API-backed, local fallback, seed data, inaccessible, or blocked. -- [x] 5.4 In the browser, exercise or inspect server lifecycle controls, plugin marketplace actions, user create/status actions, AI provider create/test/status actions, config diff/write dispatch, log history, artifact download/file transfer, and plugin bridge actions where available. -- [x] 5.5 Verify visible UI state does not expose raw host paths, run credentials, direct sockets, raw AI keys, or plugin-owned transport details. - -## 6. Baseline Classification and Queue Handoff - -- [x] 6.1 Complete the proof report with command output summaries and browser walkthrough notes. -- [x] 6.2 Identify follow-up implementation OpenSpecs for every partial, demo-only, or blocked required flow. -- [x] 6.3 Run `scripts/check-structure.sh` and record evidence. -- [x] 6.4 Run `openspec validate verify-current-platform-e2e-baseline --strict` and record evidence. -- [x] 6.5 Update `openspec/changes/architecture-delivery-stream/delivery-plan.md` and `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` only after implementation evidence exists, then stop without starting another backlog item. - -## Evidence - -- 2026-07-08: `cd platform && go test ./... -count=1` passed; packages `api`, `config`, `domain`, `dto`, `model`, `repo`, `service`, and `validator` reported `ok`. -- 2026-07-08: `cd run && go test ./... -count=1` passed; packages `api`, `config`, `protocol`, `runtime`, and `spool` reported `ok`. -- 2026-07-08: `cd plugins && npm run typecheck` passed. -- 2026-07-08: `cd plugins && npm run test` passed; Vitest reported 1 file / 10 tests. -- 2026-07-08: `cd plugins && npm run validate:manifest` initially failed inside the sandbox with `listen EPERM` for the `tsx` IPC pipe, then passed with approved escalation and printed `validated examples/dev-game-plugin/manifest.json`. -- 2026-07-08: `cd platform_web && npm run typecheck` passed. -- 2026-07-08: `cd platform_web && npm test` passed; Vitest reported 11 files / 47 tests. -- 2026-07-08: `cd platform_web && npm run build` passed; Vite built `dist/` assets. -- 2026-07-08: Browser walkthrough required `cd platform_web && VITE_ENABLE_LOCAL_AUTH_FALLBACK=true npm run dev -- --port 5173`; sandbox run failed with `listen EPERM 127.0.0.1:5173`, then approved escalation served the frontend at `http://127.0.0.1:5174/`. -- 2026-07-08: Browser auth page showed `本地回退可用` and `进入本地回退工作台`, proving the local browser stack did not have real auth API backing. -- 2026-07-08: Browser fallback server workspace at `#/servers` showed `服务器列表加载失败` with `path /api/v1/jobs was not found`. -- 2026-07-08: Browser routes `#/home`, `#/plugins`, `#/users`, and `#/aiProviders` rendered/redirected to the fallback server workspace for the server-admin fallback user, so platform-admin first-party areas remain browser-blocked in this baseline. -- 2026-07-08: Browser `#/profile` rendered `个人设置` as `本地会话` with editable profile/theme controls; this proves local fallback UI only, not API persistence. -- 2026-07-08: `openspec/changes/verify-current-platform-e2e-baseline/proof-report.md` records the classification matrix and follow-up recommendations. -- 2026-07-08: `scripts/check-structure.sh` passed. -- 2026-07-08: `openspec validate verify-current-platform-e2e-baseline --strict` reported the change is valid; PostHog telemetry flush failed due restricted DNS and did not affect validation. -- 2026-07-08: Updated `openspec/changes/architecture-delivery-stream/delivery-plan.md` to mark `verify-current-platform-e2e-baseline` complete and `fix-env-profile-settings` blocked; updated `openspec/changes/architecture-delivery-stream/NEXT_CHANGE.md` to point at generating `implement-real-game-plugin-lifecycle-proof`. diff --git a/openspec/config.yaml b/openspec/config.yaml deleted file mode 100644 index 392946c..0000000 --- a/openspec/config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -schema: spec-driven - -# Project context (optional) -# This is shown to AI when creating artifacts. -# Add your tech stack, conventions, style guides, domain knowledge, etc. -# Example: -# context: | -# Tech stack: TypeScript, React, Node.js -# We use conventional commits -# Domain: e-commerce platform - -# Per-artifact rules (optional) -# Add custom rules for specific artifacts. -# Example: -# rules: -# proposal: -# - Keep proposals under 500 words -# - Always include a "Non-goals" section -# tasks: -# - Break tasks into chunks of max 2 hours diff --git a/openspec/specs/platform-side-distribution-builds/spec.md b/openspec/specs/platform-side-distribution-builds/spec.md deleted file mode 100644 index 3e732a1..0000000 --- a/openspec/specs/platform-side-distribution-builds/spec.md +++ /dev/null @@ -1,71 +0,0 @@ -# platform-side-distribution-builds Specification - -## Purpose -TBD - created by archiving change platform-side-docker-distribution-builds. Update Purpose after archive. -## Requirements -### Requirement: Server creation requires only plugin type and server name -The system SHALL require only the game plugin type and the server name to create a server instance, and SHALL NOT require a deployment target, run endpoint, or runtime profile at creation time. - -#### Scenario: Creation form field set -- **WHEN** an owner opens the server creation workflow -- **THEN** the form requires plugin type and server name only, and presents no deployment target or run endpoint selector as a creation prerequisite - -#### Scenario: Creation without any registered endpoint -- **WHEN** an owner creates a server instance while no run endpoint is registered for that instance -- **THEN** creation succeeds and the instance is created without a deployment target binding - -#### Scenario: Binding established by run registration -- **WHEN** a generated run for that instance registers itself with the platform -- **THEN** the platform binds the instance to that run endpoint without the owner having pre-selected it - -#### Scenario: Target selection remains available after creation -- **WHEN** an owner opens an already-created instance -- **THEN** deployment target and runtime profile selection remain available as optional actions on that instance - -### Requirement: Distribution builds execute in a platform-owned Docker builder -The platform SHALL execute `distribution.build` work in a platform-owned Docker builder and SHALL NOT dispatch distribution build jobs to machine-side run endpoints. - -#### Scenario: Run distribution build execution -- **WHEN** an owner requests run generation for a server instance -- **THEN** the platform builds the package in its own Docker builder and records the resulting artifact against the build job - -#### Scenario: Client-manager distribution build execution -- **WHEN** an owner requests client-manager generation for a server instance -- **THEN** the platform builds the package in its own Docker builder and records the resulting artifact against the build job - -#### Scenario: Build failure reporting -- **WHEN** a platform Docker build fails -- **THEN** the distribution status becomes failed, the build job reports a failure, and the failure reason excludes host paths and secret values - -### Requirement: Build availability is independent of run endpoint capabilities -The system SHALL determine `generate-run` and `generate-client-manager` availability from plugin declarations, runtime bindings, and platform builder readiness, and SHALL NOT require any run endpoint to advertise `distribution.build`. - -#### Scenario: Instance bound only to its own generated run -- **WHEN** a server instance's only run endpoint is its own generated run, which holds no distribution-build authority -- **THEN** `generate-run` remains available and a new run distribution can be generated - -#### Scenario: No privileged worker endpoint registered -- **WHEN** no run endpoint advertising `distribution.build` is registered or online -- **THEN** run generation still succeeds through the platform Docker builder - -#### Scenario: Builder unavailable -- **WHEN** the platform Docker builder is unavailable -- **THEN** the unavailable reason names the platform builder rather than a run endpoint capability - -### Requirement: Generated runs hold no distribution-build authority -The system SHALL continue to deny distribution-build work to component-authenticated generated runs. This restriction is a security boundary and SHALL NOT be relaxed to unblock building. - -#### Scenario: Generated run claims a build -- **WHEN** a component-authenticated generated run claims work advertising `distribution.build` -- **THEN** the platform does not assign distribution build work to that run - -### Requirement: Platform builds do not expose plaintext component auth keys over the job channel -The system SHALL keep component auth keys inside the platform when builds are executed by the platform Docker builder, and SHALL NOT return plaintext auth keys to machine-side run endpoints for distribution builds. - -#### Scenario: Build input secret handling -- **WHEN** the platform builder assembles a package requiring a component auth key -- **THEN** the key is resolved inside the platform and is not transmitted to any machine-side run endpoint - -#### Scenario: Generated package still authenticates -- **WHEN** a package built by the platform builder registers with the platform -- **THEN** its embedded credential and key generation are accepted as before diff --git a/platform/api/resource_handlers_test.go b/platform/api/resource_handlers_test.go index 648fbbf..6110b58 100644 --- a/platform/api/resource_handlers_test.go +++ b/platform/api/resource_handlers_test.go @@ -640,16 +640,10 @@ func TestServerLifecycleWorkflowAPI(t *testing.T) { if created.Action != domain.ServerLifecycleActionCreate || created.Instance.State != domain.ServerInstanceStateDraft || created.Job.ID != "" || created.Instance.RunEndpointID != "" { t.Fatalf("expected create workflow response, got %+v", created) } - legacyCreate := requestJSONWithAuth(t, router, http.MethodPost, "/api/v1/server-instances/workflows/create", dto.ServerLifecycleCreateRequest{ - ID: "server-create-legacy", - PluginID: "server.scum", - RunEndpointID: "run-local", - Name: "SCUM Legacy Create", - IdempotencyKey: "idem-create-legacy", - ProfileKey: "local", - Bindings: map[string]string{"server-root": "runtime.server-root"}, + legacyCreate := requestJSONWithAuth(t, router, http.MethodPost, "/api/v1/server-instances/workflows/create", map[string]any{ + "id": "server-create-legacy", "pluginId": "server.scum", "runEndpointId": "run-local", "name": "SCUM Legacy Create", "idempotencyKey": "idem-create-legacy", "profileKey": "local", "bindings": map[string]string{"server-root": "runtime.server-root"}, }, adminSession) - assertErrorResponse(t, legacyCreate, http.StatusBadRequest, errorCodeValidation) + assertErrorResponse(t, legacyCreate, http.StatusBadRequest, errorCodeBadRequest) ready := postJSONWithAuth[dto.ServerInstanceResponse](t, router, "/api/v1/server-instances", dto.ServerInstanceCreateRequest{ ID: "server-ready", @@ -1492,10 +1486,10 @@ func TestRuntimeBindingAPIIsAuthorizedValidatedAndRedacted(t *testing.T) { undeclared := requestJSONWithAuth(t, router, http.MethodPut, "/api/v1/server-instances/"+server.ID+"/runtime-binding", dto.RuntimeBindingUpdateRequest{ProfileKey: "local", Bindings: map[string]string{"host.socket": "runtime.socket"}}, adminSession) assertErrorResponse(t, undeclared, http.StatusBadRequest, errorCodeValidation) - legacyCreate := requestJSONWithAuth(t, router, http.MethodPost, "/api/v1/server-instances/workflows/create", dto.ServerLifecycleCreateRequest{ID: "runtime-create-complete", PluginID: registration.Manifest.ID, RunEndpointID: "run-local", Name: "Runtime Create Complete", IdempotencyKey: "runtime-create-complete", ProfileKey: "local", Bindings: map[string]string{"server-root": "runtime.server-root", "rcon.password": "secret://runtime-create-complete/rcon"}}, adminSession) - assertErrorResponse(t, legacyCreate, http.StatusBadRequest, errorCodeValidation) - incompleteCreate := requestJSONWithAuth(t, router, http.MethodPost, "/api/v1/server-instances/workflows/create", dto.ServerLifecycleCreateRequest{ID: "runtime-create-incomplete", PluginID: registration.Manifest.ID, RunEndpointID: "run-local", Name: "Runtime Create Incomplete", IdempotencyKey: "runtime-create-incomplete", ProfileKey: "local", Bindings: map[string]string{"server-root": "runtime.server-root"}}, adminSession) - assertErrorResponse(t, incompleteCreate, http.StatusBadRequest, errorCodeValidation) + legacyCreate := requestJSONWithAuth(t, router, http.MethodPost, "/api/v1/server-instances/workflows/create", map[string]any{"id": "runtime-create-complete", "pluginId": registration.Manifest.ID, "runEndpointId": "run-local", "name": "Runtime Create Complete", "idempotencyKey": "runtime-create-complete", "profileKey": "local", "bindings": map[string]string{"server-root": "runtime.server-root", "rcon.password": "secret://runtime-create-complete/rcon"}}, adminSession) + assertErrorResponse(t, legacyCreate, http.StatusBadRequest, errorCodeBadRequest) + incompleteCreate := requestJSONWithAuth(t, router, http.MethodPost, "/api/v1/server-instances/workflows/create", map[string]any{"id": "runtime-create-incomplete", "pluginId": registration.Manifest.ID, "runEndpointId": "run-local", "name": "Runtime Create Incomplete", "idempotencyKey": "runtime-create-incomplete", "profileKey": "local", "bindings": map[string]string{"server-root": "runtime.server-root"}}, adminSession) + assertErrorResponse(t, incompleteCreate, http.StatusBadRequest, errorCodeBadRequest) missingServer := requestWithAuth(t, router, http.MethodGet, "/api/v1/server-instances/runtime-create-incomplete", "", adminSession) assertErrorResponse(t, missingServer, http.StatusNotFound, errorCodeNotFound) } diff --git a/platform/api/routes.md b/platform/api/routes.md index 2c7e9c7..8bcf39a 100644 --- a/platform/api/routes.md +++ b/platform/api/routes.md @@ -135,7 +135,7 @@ Artifact bridge execution returns safe metadata and platform content routes only ## Implemented Server Lifecycle Actions -- `POST /api/v1/server-instances/workflows/create`: accepts `ServerLifecycleCreateRequest`. Creation starts from `pluginId` and `name`, and may include the create-wizard deployment definition such as deployment mode, plugin create inputs, server root, or custom start command. The browser must not require or submit a deployment target, run endpoint, runtime profile, or runtime bindings during creation; those bindings remain post-creation/runtime-registration concerns. Requests that include `deploymentTargetId`, `runEndpointId`, `profileKey`, `bindings`, `deployment.runEndpointId`, `deployment.profileKey`, or `deployment.runtimeBindings` are rejected. +- `POST /api/v1/server-instances/workflows/create`: accepts `ServerLifecycleCreateRequest`. Creation starts from `pluginId` and `name`, and may include the create-wizard deployment definition such as deployment mode, plugin create inputs, server root, or custom start command. The browser does not submit a deployment target, Run endpoint, lifecycle profile, or Run identity binding. Platform applies plugin defaults, creates the definition without waiting for a Run, and attaches the active Run when its authenticated heartbeat arrives. Unknown legacy binding fields are rejected by the strict JSON decoder. - `POST /api/v1/server-instances/{id}/start`: accept `ServerLifecycleCommandRequest`, validate state/config version/run capability, and queue a `process.start` job using `ServerLifecycleResponse`. - `POST /api/v1/server-instances/{id}/stop`: accept `ServerLifecycleCommandRequest`, validate state/config version/run capability, and queue a `process.stop` job using `ServerLifecycleResponse`. diff --git a/platform/api/server_lifecycle_handlers.go b/platform/api/server_lifecycle_handlers.go index ed8c390..ec89a80 100644 --- a/platform/api/server_lifecycle_handlers.go +++ b/platform/api/server_lifecycle_handlers.go @@ -8,7 +8,7 @@ import ( // serverInstanceCreateWorkflow godoc // @Summary Create server instance workflow -// @Description Creates a server definition. A deployment target saves a draft and reserves a dedicated Run identity; installation is queued only after that Run registers and deployment is requested. +// @Description Creates a server definition. Run is discovered automatically from its heartbeat; operators do not select a node or lifecycle profile. // @Tags server-instances // @Accept json // @Produce json @@ -29,10 +29,6 @@ func (h *coreHandlers) serverInstanceCreateWorkflow(w http.ResponseWriter, r *ht writeDecodeError(w, err) return } - if err := request.ValidateCreateOnly(); err != nil { - writeServiceError(w, err) - return - } result, err := h.core.CreateServerInstanceWorkflowForSession(bearerToken(r), request.ToDomain()) if err != nil { writeServiceError(w, err) @@ -83,7 +79,7 @@ func (h *coreHandlers) serverDeployment(w http.ResponseWriter, r *http.Request) } } -// serverInstanceDeploy binds an existing draft definition to its configured Run and queues install. +// serverInstanceDeploy is retained for the internal lifecycle compatibility route; new Run packages execute their autonomous plan and report heartbeat/activity without an operator binding step. func (h *coreHandlers) serverInstanceDeploy(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeMethodNotAllowed(w, http.MethodPost) diff --git a/platform/domain/resources.go b/platform/domain/resources.go index 0af779c..f65d16f 100644 --- a/platform/domain/resources.go +++ b/platform/domain/resources.go @@ -771,9 +771,8 @@ type ServerInstance struct { ID string PluginID string PluginVersion string - // DeploymentTargetID identifies an optional operator-selected deployment - // target for post-creation deployment operations. It never selects a - // distribution builder or replaces the server's generated Run endpoint. + // DeploymentTargetID is retained only for loading legacy snapshots. New API + // flows never write or expose an operator-selected deployment target. DeploymentTargetID string RunEndpointID string Name string diff --git a/platform/dto/resources.go b/platform/dto/resources.go index 8029e64..3419ea9 100644 --- a/platform/dto/resources.go +++ b/platform/dto/resources.go @@ -354,7 +354,7 @@ type GameClientBridgeDataPackDeclarationBody struct { DatabaseUserVersion int `json:"databaseUserVersion"` LogParserRefs []string `json:"logParserRefs"` ConfigMapRefs []string `json:"configMapRefs"` - DataRefs []string `json:"dataRefs,omitempty"` + DataRefs []string `json:"dataRefs,omitempty"` } type GameClientBridgeOperationSafetyBody struct { @@ -659,7 +659,6 @@ type ServerInstanceResponse struct { ID string `json:"id"` PluginID string `json:"pluginId"` PluginVersion string `json:"pluginVersion"` - DeploymentTargetID string `json:"deploymentTargetId,omitempty"` RunEndpointID string `json:"runEndpointId"` Name string `json:"name"` OwnerUserID string `json:"ownerUserId,omitempty"` @@ -1245,7 +1244,7 @@ func (body GameClientBridgeManifestBody) ToDomain() domain.GameClientBridgeManif } dataPacks := make([]domain.GameClientBridgeDataPackDeclaration, len(body.DataPacks)) for index, dataPack := range body.DataPacks { - dataPacks[index] = domain.GameClientBridgeDataPackDeclaration{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs), DataRefs: domain.CopyStringSlice(dataPack.DataRefs)} + dataPacks[index] = domain.GameClientBridgeDataPackDeclaration{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs), DataRefs: domain.CopyStringSlice(dataPack.DataRefs)} } operationTemplates := make([]domain.GameClientBridgeOperationTemplateDeclaration, len(body.OperationTemplates)) for index, template := range body.OperationTemplates { @@ -1731,7 +1730,7 @@ func gameClientBridgeManifestFromDomain(value domain.GameClientBridgeManifest) G } dataPacks := make([]GameClientBridgeDataPackDeclarationBody, len(value.DataPacks)) for index, dataPack := range value.DataPacks { - dataPacks[index] = GameClientBridgeDataPackDeclarationBody{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs), DataRefs: domain.CopyStringSlice(dataPack.DataRefs)} + dataPacks[index] = GameClientBridgeDataPackDeclarationBody{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs), DataRefs: domain.CopyStringSlice(dataPack.DataRefs)} } operationTemplates := make([]GameClientBridgeOperationTemplateDeclarationBody, len(value.OperationTemplates)) for index, template := range value.OperationTemplates { @@ -1820,7 +1819,6 @@ func ServerInstanceFromDomain(instance domain.ServerInstance) ServerInstanceResp ID: instance.ID, PluginID: instance.PluginID, PluginVersion: instance.PluginVersion, - DeploymentTargetID: instance.DeploymentTargetID, RunEndpointID: instance.RunEndpointID, Name: instance.Name, OwnerUserID: instance.OwnerUserID, diff --git a/platform/dto/server_lifecycle.go b/platform/dto/server_lifecycle.go index 61bca37..b8b2fd3 100644 --- a/platform/dto/server_lifecycle.go +++ b/platform/dto/server_lifecycle.go @@ -1,18 +1,13 @@ package dto import ( - "strings" "time" "browser.local/platform/domain" - "browser.local/platform/validator" ) type ServerDeploymentRequest struct { - RunEndpointID string `json:"runEndpointId,omitempty"` Mode domain.ServerDeploymentMode `json:"mode"` - ProfileKey string `json:"profileKey,omitempty"` - RuntimeBindings map[string]string `json:"runtimeBindings,omitempty"` CreateInputs map[string]string `json:"createInputs,omitempty"` ServerRoot string `json:"serverRoot,omitempty"` WorkingDirectory string `json:"workingDirectory,omitempty"` @@ -27,7 +22,6 @@ type ServerDeploymentRequest struct { type ServerDeploymentResponse struct { ServerInstanceID string `json:"serverInstanceId"` Mode domain.ServerDeploymentMode `json:"mode,omitempty"` - ProfileKey string `json:"profileKey,omitempty"` CreateInputs map[string]string `json:"createInputs,omitempty"` ServerRootConfigured bool `json:"serverRootConfigured"` WorkingDirectoryConfigured bool `json:"workingDirectoryConfigured"` @@ -77,45 +71,12 @@ type ServerDeploymentProjectionBody struct { } type ServerLifecycleCreateRequest struct { - ID string `json:"id"` - PluginID string `json:"pluginId"` - DeploymentTargetID string `json:"deploymentTargetId,omitempty"` - RunEndpointID string `json:"runEndpointId,omitempty"` - Name string `json:"name"` - OwnerUserID string `json:"ownerUserId,omitempty"` - IdempotencyKey string `json:"idempotencyKey"` - ProfileKey string `json:"profileKey"` - Bindings map[string]string `json:"bindings,omitempty"` - Deployment ServerDeploymentRequest `json:"deployment,omitempty"` -} - -func (request ServerLifecycleCreateRequest) ValidateCreateOnly() error { - var violations []string - if strings.TrimSpace(request.DeploymentTargetID) != "" { - violations = append(violations, "deploymentTargetId must not be provided during server creation") - } - if strings.TrimSpace(request.RunEndpointID) != "" { - violations = append(violations, "runEndpointId must not be provided during server creation") - } - if strings.TrimSpace(request.ProfileKey) != "" { - violations = append(violations, "profileKey must not be provided during server creation") - } - if len(request.Bindings) > 0 { - violations = append(violations, "bindings must not be provided during server creation") - } - if strings.TrimSpace(request.Deployment.RunEndpointID) != "" { - violations = append(violations, "deployment.runEndpointId must not be provided during server creation") - } - if strings.TrimSpace(request.Deployment.ProfileKey) != "" { - violations = append(violations, "deployment.profileKey must not be provided during server creation") - } - if len(request.Deployment.RuntimeBindings) > 0 { - violations = append(violations, "deployment.runtimeBindings must not be provided during server creation") - } - if len(violations) > 0 { - return validator.ValidationError{Violations: violations} - } - return nil + ID string `json:"id"` + PluginID string `json:"pluginId"` + Name string `json:"name"` + OwnerUserID string `json:"ownerUserId,omitempty"` + IdempotencyKey string `json:"idempotencyKey"` + Deployment ServerDeploymentRequest `json:"deployment,omitempty"` } type ServerLifecycleCommandRequest struct { @@ -132,21 +93,17 @@ type ServerLifecycleResponse struct { func (request ServerLifecycleCreateRequest) ToDomain() domain.ServerLifecycleCreate { return domain.ServerLifecycleCreate{ - ID: request.ID, - PluginID: request.PluginID, - DeploymentTargetID: "", - RunEndpointID: "", - Name: request.Name, - OwnerUserID: request.OwnerUserID, - IdempotencyKey: request.IdempotencyKey, - ProfileKey: "", - Bindings: nil, - Deployment: request.Deployment.deploymentDefinition(), + ID: request.ID, + PluginID: request.PluginID, + Name: request.Name, + OwnerUserID: request.OwnerUserID, + IdempotencyKey: request.IdempotencyKey, + Deployment: request.Deployment.deploymentDefinition(), } } func (request ServerDeploymentRequest) ToDomain() domain.ServerDeploymentUpdate { - update := domain.ServerDeploymentUpdate{RunEndpointID: request.RunEndpointID, Mode: request.Mode, ProfileKey: request.ProfileKey, RuntimeBindings: domain.CopyStringMap(request.RuntimeBindings), CreateInputs: domain.CopyStringMap(request.CreateInputs), ServerRoot: request.ServerRoot, WorkingDirectory: request.WorkingDirectory, InstallCommand: request.InstallCommand, StartCommand: request.StartCommand, StopCommand: request.StopCommand, StatusCommand: request.StatusCommand, ClearFields: domain.CopyStringSlice(request.ClearFields)} + update := domain.ServerDeploymentUpdate{Mode: request.Mode, CreateInputs: domain.CopyStringMap(request.CreateInputs), ServerRoot: request.ServerRoot, WorkingDirectory: request.WorkingDirectory, InstallCommand: request.InstallCommand, StartCommand: request.StartCommand, StopCommand: request.StopCommand, StatusCommand: request.StatusCommand, ClearFields: domain.CopyStringSlice(request.ClearFields)} if request.Shell != nil { update.Shell, update.ShellSet = *request.Shell, true } @@ -155,11 +112,11 @@ func (request ServerDeploymentRequest) ToDomain() domain.ServerDeploymentUpdate func (request ServerDeploymentRequest) deploymentDefinition() domain.ServerDeploymentDefinition { update := request.ToDomain() - return domain.ServerDeploymentDefinition{Mode: update.Mode, ProfileKey: update.ProfileKey, RuntimeBindings: update.RuntimeBindings, CreateInputs: update.CreateInputs, ServerRoot: update.ServerRoot, WorkingDirectory: update.WorkingDirectory, InstallCommand: update.InstallCommand, StartCommand: update.StartCommand, StopCommand: update.StopCommand, StatusCommand: update.StatusCommand, Shell: update.Shell} + return domain.ServerDeploymentDefinition{Mode: update.Mode, CreateInputs: update.CreateInputs, ServerRoot: update.ServerRoot, WorkingDirectory: update.WorkingDirectory, InstallCommand: update.InstallCommand, StartCommand: update.StartCommand, StopCommand: update.StopCommand, StatusCommand: update.StatusCommand, Shell: update.Shell} } func ServerDeploymentFromDomain(view domain.ServerDeploymentView) ServerDeploymentResponse { - return ServerDeploymentResponse{ServerInstanceID: view.ServerInstanceID, Mode: view.Mode, ProfileKey: view.ProfileKey, CreateInputs: domain.CopyStringMap(view.CreateInputs), ServerRootConfigured: view.ServerRootConfigured, WorkingDirectoryConfigured: view.WorkingDirectoryConfigured, InstallCommandConfigured: view.InstallCommandConfigured, StartCommandConfigured: view.StartCommandConfigured, StopCommandConfigured: view.StopCommandConfigured, StatusCommandConfigured: view.StatusCommandConfigured, Shell: view.Shell, Revision: view.Revision, UpdatedAt: optionalTime(view.UpdatedAt), Projection: deploymentProjectionFromDomain(view.Projection), LatestDispatch: deploymentDispatchEvidenceFromDomain(view.LatestDispatch)} + return ServerDeploymentResponse{ServerInstanceID: view.ServerInstanceID, Mode: view.Mode, CreateInputs: domain.CopyStringMap(view.CreateInputs), ServerRootConfigured: view.ServerRootConfigured, WorkingDirectoryConfigured: view.WorkingDirectoryConfigured, InstallCommandConfigured: view.InstallCommandConfigured, StartCommandConfigured: view.StartCommandConfigured, StopCommandConfigured: view.StopCommandConfigured, StatusCommandConfigured: view.StatusCommandConfigured, Shell: view.Shell, Revision: view.Revision, UpdatedAt: optionalTime(view.UpdatedAt), Projection: deploymentProjectionFromDomain(view.Projection), LatestDispatch: deploymentDispatchEvidenceFromDomain(view.LatestDispatch)} } func ServerDeploymentRevealFromDomain(reveal domain.ServerDeploymentReveal) ServerDeploymentRevealResponse { diff --git a/platform/model/resources.go b/platform/model/resources.go index d52be0a..5669b6c 100644 --- a/platform/model/resources.go +++ b/platform/model/resources.go @@ -204,8 +204,8 @@ type ServerInstance struct { PluginID string `json:"pluginId" db:"plugin_id"` // PluginVersion records the plugin version used for creation or reconcile. PluginVersion string `json:"pluginVersion" db:"plugin_version"` - // DeploymentTargetID references an optional post-creation deployment target; - // platform-owned distribution builds never use it as a builder selector. + // DeploymentTargetID is retained only for legacy snapshots. Current flows + // attach Run identity from authenticated heartbeats instead of selecting it. DeploymentTargetID string `json:"deploymentTargetId,omitempty" db:"deployment_target_id"` // RunEndpointID references the dedicated server Run endpoint. RunEndpointID string `json:"runEndpointId" db:"run_endpoint_id"` diff --git a/platform/service/control.go b/platform/service/control.go index be6096e..e8f8ade 100644 --- a/platform/service/control.go +++ b/platform/service/control.go @@ -30,7 +30,7 @@ func (svc *CoreService) RegisterRunHello(hello domain.RunControlHello) (domain.R return domain.RunControlHelloResult{}, err } if hasComponentAuthIdentity(hello) { - if err := svc.validateDedicatedRunHello(hello); err != nil { + if err := svc.validateAuthenticatedRunHello(hello); err != nil { return domain.RunControlHelloResult{}, err } auth, err := svc.AuthenticateComponent(domain.ComponentAuthenticationRequest{ @@ -76,6 +76,19 @@ func (svc *CoreService) RegisterRunHello(hello domain.RunControlHello) (domain.R if err := svc.upsertRunEndpoint(endpoint); err != nil { return domain.RunControlHelloResult{}, err } + if hello.ServerInstanceID != "" && hello.ComponentKind == domain.DistributionComponentRun { + instance, instanceErr := svc.store.ServerInstances().Get(hello.ServerInstanceID) + if instanceErr != nil { + return domain.RunControlHelloResult{}, instanceErr + } + if instance.RunEndpointID != hello.RunEndpointID { + instance.RunEndpointID = hello.RunEndpointID + instance.UpdatedAt = stamp + if err := svc.store.ServerInstances().Update(instance); err != nil { + return domain.RunControlHelloResult{}, err + } + } + } previous, previousErr := svc.store.RunControlSessions().Get(hello.RunEndpointID) generation := 1 if previousErr == nil { @@ -126,19 +139,22 @@ func (svc *CoreService) RegisterRunHello(hello domain.RunControlHello) (domain.R }), nil } -func (svc *CoreService) validateDedicatedRunHello(hello domain.RunControlHello) error { +func (svc *CoreService) validateAuthenticatedRunHello(hello domain.RunControlHello) error { if hello.ComponentKind != domain.DistributionComponentRun { return validationError("component-authenticated run hello must use the run component") } + if hello.RunEndpointID == platformDistributionBuilderEndpointID { + return validationError("platform distribution builder cannot register as a server Run") + } instance, err := svc.store.ServerInstances().Get(hello.ServerInstanceID) if err != nil { return err } - if strings.TrimSpace(instance.DeploymentTargetID) == "" && instance.RunEndpointID != dedicatedRunEndpointID(instance.ID) { - return nil // legacy Run registrations keep their historical endpoint contract. + if hello.PluginID != instance.PluginID { + return validationError("Run plugin identity does not match the server instance") } - if hello.PluginID != instance.PluginID || hello.RunEndpointID != instance.RunEndpointID { - return validationError("run endpoint identity does not match the server binding") + if instance.State == domain.ServerInstanceStateDeleted { + return validationError("deleted server cannot attach an active Run heartbeat") } instances, err := svc.store.ServerInstances().List(domain.ServerInstanceFilter{RunEndpointID: hello.RunEndpointID}) if err != nil { diff --git a/platform/service/control_test.go b/platform/service/control_test.go index ad11af4..27e4145 100644 --- a/platform/service/control_test.go +++ b/platform/service/control_test.go @@ -205,7 +205,6 @@ func TestCoreServiceRunHelloRejectsStalePackageKeyAfterReset(t *testing.T) { func TestCoreServiceRunHelloRejectsGeneratedRunOnPromotedBuildEndpoint(t *testing.T) { svc, session, instance := newDistributionTestFixture(t) - builderID := instance.RunEndpointID instance.State = domain.ServerInstanceStateFailed if err := svc.store.ServerInstances().Update(instance); err != nil { t.Fatalf("mark legacy server failed: %v", err) @@ -213,28 +212,28 @@ func TestCoreServiceRunHelloRejectsGeneratedRunOnPromotedBuildEndpoint(t *testin if _, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ServerInstanceID: instance.ID, TargetOS: "windows", TargetArch: "amd64", IdempotencyKey: "promoted-hello-fence"}); err != nil { t.Fatalf("generate promoted Run: %v", err) } - migrated, err := svc.GetServerInstance(instance.ID) - if err != nil { - t.Fatalf("get migrated server: %v", err) - } key, plainKey, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentRun, "") if err != nil { t.Fatalf("get component key: %v", err) } hello := validRunControlHello() - hello.RunEndpointID = builderID + hello.RunEndpointID = platformDistributionBuilderEndpointID hello.RegistrationToken = plainKey hello.ServerInstanceID = instance.ID hello.PluginID = instance.PluginID hello.ComponentKind = domain.DistributionComponentRun hello.KeyGeneration = key.Generation - if _, err := svc.RegisterRunHello(hello); err == nil || !strings.Contains(err.Error(), "does not match") { + if _, err := svc.RegisterRunHello(hello); err == nil || !strings.Contains(err.Error(), "platform distribution builder") { t.Fatalf("expected shared builder registration rejection, got %v", err) } - hello.RunEndpointID = migrated.RunEndpointID + hello.RunEndpointID = generatedRunEndpointID(instance.ID) if result, err := svc.RegisterRunHello(hello); err != nil || !result.Accepted { - t.Fatalf("expected dedicated Run registration acceptance, result=%+v err=%v", result, err) + t.Fatalf("expected automatically discovered Run registration acceptance, result=%+v err=%v", result, err) + } + attached, err := svc.GetServerInstance(instance.ID) + if err != nil || attached.RunEndpointID != hello.RunEndpointID { + t.Fatalf("expected heartbeat to attach active Run endpoint, instance=%+v err=%v", attached, err) } } diff --git a/platform/service/distribution_build_execution_test.go b/platform/service/distribution_build_execution_test.go index 72b8453..4a05ed1 100644 --- a/platform/service/distribution_build_execution_test.go +++ b/platform/service/distribution_build_execution_test.go @@ -274,19 +274,11 @@ func TestCoreServiceGeneratedRunOnlyEndpointCanGenerateAnotherRun(t *testing.T) packageConfig := readGeneratedPackageConfig(t, svc, session, first.ArtifactID) instance, err = svc.GetServerInstance(instance.ID) if err != nil { - t.Fatalf("get dedicated Run binding: %v", err) - } - bootstrap, err := svc.store.RunEndpoints().Get(instance.DeploymentTargetID) - if err != nil { - t.Fatalf("get former bootstrap endpoint: %v", err) - } - bootstrap.Status = domain.RunEndpointStatusOffline - if err := svc.store.RunEndpoints().Update(bootstrap); err != nil { - t.Fatalf("take former bootstrap endpoint offline: %v", err) + t.Fatalf("get server after Run build: %v", err) } helloRequest := validRunControlHello() - helloRequest.RunEndpointID = instance.RunEndpointID + helloRequest.RunEndpointID = first.RunEndpointID helloRequest.RegistrationToken = packageConfig.AuthKey helloRequest.ServerInstanceID = instance.ID helloRequest.PluginID = instance.PluginID @@ -304,7 +296,7 @@ func TestCoreServiceGeneratedRunOnlyEndpointCanGenerateAnotherRun(t *testing.T) t.Fatalf("register generated Run: result=%+v err=%v", registered, err) } online, err := svc.store.RunEndpoints().List(domain.RunEndpointFilter{Status: domain.RunEndpointStatusOnline}) - if err != nil || len(online) != 1 || online[0].ID != instance.RunEndpointID { + if err != nil || len(online) != 1 || online[0].ID != first.RunEndpointID { t.Fatalf("expected generated Run to be the only online endpoint: endpoints=%+v err=%v", online, err) } for _, capability := range online[0].Capabilities { @@ -313,7 +305,7 @@ func TestCoreServiceGeneratedRunOnlyEndpointCanGenerateAnotherRun(t *testing.T) } } claim, err := svc.ClaimRunJob(domain.RunJobClaim{ - RunEndpointID: instance.RunEndpointID, + RunEndpointID: first.RunEndpointID, SessionToken: registered.SessionToken, Capabilities: []string{domain.JobCapabilityDistributionBuild}, Capacity: domain.RunCapacity{MaxJobs: 1}, diff --git a/platform/service/distributions.go b/platform/service/distributions.go index c8aeb52..25b47d5 100644 --- a/platform/service/distributions.go +++ b/platform/service/distributions.go @@ -46,9 +46,6 @@ func (svc *CoreService) GenerateRunDistributionForSession(sessionID string, requ return domain.RunDistribution{}, err } } - if err := svc.promoteLegacyRunBinding(&instance); err != nil { - return domain.RunDistribution{}, err - } if ready, reason := svc.distributionBuilderReadiness(); !ready { _ = svc.recordAuditEvent(user.ID, "run.generate.denied", "server-instance", instance.ID, domain.AuditResultDenied, reason) return domain.RunDistribution{}, validationError(reason) @@ -72,7 +69,7 @@ func (svc *CoreService) GenerateRunDistributionForSession(sessionID string, requ ID: distributionID, ServerInstanceID: instance.ID, PluginID: plugin.ID, - RunEndpointID: instance.RunEndpointID, + RunEndpointID: runEndpointIDForDistribution(instance), TargetOS: request.TargetOS, TargetArch: request.TargetArch, PackageFormat: runPackageFormatForTarget(request.TargetOS), @@ -123,29 +120,6 @@ func (svc *CoreService) GenerateRunDistributionForSession(sessionID string, requ return domain.CopyRunDistribution(distribution), nil } -// promoteLegacyRunBinding reserves the server-scoped endpoint used by a -// generated Run. A legacy shared endpoint remains an optional deployment target -// for non-build workflows; distribution builds are always platform-owned. -func (svc *CoreService) promoteLegacyRunBinding(instance *domain.ServerInstance) error { - if instance == nil || strings.TrimSpace(instance.DeploymentTargetID) != "" || (instance.State != domain.ServerInstanceStateDraft && instance.State != domain.ServerInstanceStateFailed) { - return nil - } - currentEndpointID := strings.TrimSpace(instance.RunEndpointID) - dedicatedEndpointID := dedicatedRunEndpointID(instance.ID) - if currentEndpointID == dedicatedEndpointID { - return nil - } - if currentEndpointID != "" { - instance.DeploymentTargetID = currentEndpointID - } - instance.RunEndpointID = dedicatedEndpointID - instance.UpdatedAt = svc.now() - if err := validator.ValidateServerInstance(*instance); err != nil { - return err - } - return svc.store.ServerInstances().Update(*instance) -} - func (svc *CoreService) GenerateClientManagerDistributionForSession(sessionID string, request domain.ClientManagerBuildRequest) (domain.ClientManagerDistribution, error) { request = domain.CopyClientManagerBuildRequest(request) if strings.TrimSpace(request.IdempotencyKey) == "" { @@ -458,9 +432,6 @@ func (svc *CoreService) GetServerRuntimeActionsForSession(sessionID string, serv } endpoint, endpointErr := svc.store.RunEndpoints().Get(instance.RunEndpointID) runRegistered := endpointErr == nil - if endpointErr != nil && strings.TrimSpace(instance.DeploymentTargetID) != "" { - endpoint, endpointErr = svc.store.RunEndpoints().Get(instance.DeploymentTargetID) - } if endpointErr != nil && !errors.Is(endpointErr, repo.ErrNotFound) { return domain.ServerRuntimeActions{}, endpointErr } @@ -506,15 +477,15 @@ func (svc *CoreService) GetServerRuntimeActionsForSession(sessionID string, serv Actions: []domain.ServerRuntimeAction{ runtimeAction("generate-run", "Generate run", pluginDeclares(plugin, "server.run.distribution") && builderReady && runPackageInputsComplete, fallbackReason(!pluginDeclares(plugin, "server.run.distribution"), "plugin permission is not declared", fallbackReason(!builderReady, builderReason, runPackageReason))), runtimeAction("download-run", "Download run", hasAvailableRunPackage, "run package has not been generated"), - runtimeAction("push-run-update", "Push run update", runRegistered && pluginDeclares(plugin, "server.run.distribution") && svc.endpointSupports(endpoint, domain.JobCapabilityRunSelfUpdate) && runPackageInputsComplete, fallbackReason(!runRegistered, "dedicated Run has not registered", fallbackReason(!pluginDeclares(plugin, "server.run.distribution") || !svc.endpointSupports(endpoint, domain.JobCapabilityRunSelfUpdate), "run endpoint cannot self-update", runPackageReason))), + runtimeAction("push-run-update", "Push run update", runRegistered && pluginDeclares(plugin, "server.run.distribution") && svc.endpointSupports(endpoint, domain.JobCapabilityRunSelfUpdate) && runPackageInputsComplete, fallbackReason(!runRegistered, "Run heartbeat has not been observed", fallbackReason(!pluginDeclares(plugin, "server.run.distribution") || !svc.endpointSupports(endpoint, domain.JobCapabilityRunSelfUpdate), "run endpoint cannot self-update", runPackageReason))), runtimeAction("reset-run-key", "Reset run key", pluginDeclares(plugin, "server.run.distribution"), "plugin permission is not declared"), runtimeAction("generate-client-manager", "Generate client manager", pluginDeclares(plugin, "server.client-manager.manage") && builderReady && bindingsComplete, fallbackReason(!pluginDeclares(plugin, "server.client-manager.manage"), "client-manager permission is not declared", fallbackReason(!builderReady, builderReason, bindingReason))), runtimeAction("download-client-manager", "Download client manager", hasAvailableClientPackage, "client-manager package has not been generated"), runtimeAction("reset-client-manager-key", "Reset client-manager key", pluginDeclares(plugin, "server.client-manager.manage"), "client-manager permission is not declared"), - runtimeAction("dependencies-check", "Check dependencies", runRegistered && dependencyPermissionDeclared && svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesCheck) && bindingsComplete, fallbackReason(!runRegistered, "dedicated Run has not registered", fallbackReason(!dependencyPermissionDeclared, "plugin permission is not declared", fallbackReason(!svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesCheck), "run endpoint cannot check dependencies", bindingReason)))), - runtimeAction("dependencies-install", "Install dependencies", runRegistered && dependencyPermissionDeclared && svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesInstall) && bindingsComplete, fallbackReason(!runRegistered, "dedicated Run has not registered", fallbackReason(!dependencyPermissionDeclared, "plugin permission is not declared", fallbackReason(!svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesInstall), "run endpoint cannot install dependencies", bindingReason)))), - runtimeAction("live-logs", "Live logs", runRegistered && pluginSupports(plugin, "logs.read"), fallbackReason(!runRegistered, "dedicated Run has not registered", "plugin does not declare live logs")), - runtimeAction("historical-logs", "Historical logs", runRegistered && svc.endpointSupports(endpoint, domain.JobCapabilityLogsBackfill) && bindingsComplete, fallbackReason(!runRegistered, "dedicated Run has not registered", fallbackReason(!svc.endpointSupports(endpoint, domain.JobCapabilityLogsBackfill), "run endpoint cannot backfill logs", bindingReason))), + runtimeAction("dependencies-check", "Check dependencies", runRegistered && dependencyPermissionDeclared && svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesCheck) && bindingsComplete, fallbackReason(!runRegistered, "Run heartbeat has not been observed", fallbackReason(!dependencyPermissionDeclared, "plugin permission is not declared", fallbackReason(!svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesCheck), "run endpoint cannot check dependencies", bindingReason)))), + runtimeAction("dependencies-install", "Install dependencies", runRegistered && dependencyPermissionDeclared && svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesInstall) && bindingsComplete, fallbackReason(!runRegistered, "Run heartbeat has not been observed", fallbackReason(!dependencyPermissionDeclared, "plugin permission is not declared", fallbackReason(!svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesInstall), "run endpoint cannot install dependencies", bindingReason)))), + runtimeAction("live-logs", "Live logs", runRegistered && pluginSupports(plugin, "logs.read"), fallbackReason(!runRegistered, "Run heartbeat has not been observed", "plugin does not declare live logs")), + runtimeAction("historical-logs", "Historical logs", runRegistered && svc.endpointSupports(endpoint, domain.JobCapabilityLogsBackfill) && bindingsComplete, fallbackReason(!runRegistered, "Run heartbeat has not been observed", fallbackReason(!svc.endpointSupports(endpoint, domain.JobCapabilityLogsBackfill), "run endpoint cannot backfill logs", bindingReason))), }, } if runRegistered { diff --git a/platform/service/distributions_test.go b/platform/service/distributions_test.go index a212592..9530634 100644 --- a/platform/service/distributions_test.go +++ b/platform/service/distributions_test.go @@ -155,7 +155,7 @@ func TestCoreServiceBuildsSCUMGuidedRunWithoutCompleteRuntimeBinding(t *testing. } } -func TestCoreServicePromotesLegacyRunBindingBeforeDistributionBuild(t *testing.T) { +func TestCoreServiceDoesNotPrebindLegacyRunBeforeDistributionBuild(t *testing.T) { svc, session, instance := newDistributionTestFixture(t) legacyEndpointID := instance.RunEndpointID instance.State = domain.ServerInstanceStateFailed @@ -165,18 +165,18 @@ func TestCoreServicePromotesLegacyRunBindingBeforeDistributionBuild(t *testing.T distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ServerInstanceID: instance.ID, TargetOS: "windows", TargetArch: "amd64", IdempotencyKey: "legacy-promote-build"}) if err != nil { - t.Fatalf("generate promoted legacy Run: %v", err) + t.Fatalf("generate Run without prebinding: %v", err) } migrated, err := svc.GetServerInstance(instance.ID) if err != nil { t.Fatalf("get migrated server: %v", err) } - if migrated.DeploymentTargetID != legacyEndpointID || migrated.RunEndpointID != "server-run-"+instance.ID { - t.Fatalf("expected legacy binding promotion, got %+v", migrated) + if migrated.DeploymentTargetID != "" || migrated.RunEndpointID != legacyEndpointID { + t.Fatalf("Run generation must not change the server's active endpoint, got %+v", migrated) } job, err := svc.GetJob(distribution.BuildJobID) - if err != nil || job.RunEndpointID != platformDistributionBuilderEndpointID || distribution.RunEndpointID != migrated.RunEndpointID { - t.Fatalf("expected platform build and dedicated package endpoint %q, job=%+v distribution=%+v err=%v", migrated.RunEndpointID, job, distribution, err) + if err != nil || job.RunEndpointID != platformDistributionBuilderEndpointID || distribution.RunEndpointID != legacyEndpointID { + t.Fatalf("expected platform build and unchanged package endpoint %q, job=%+v distribution=%+v err=%v", legacyEndpointID, job, distribution, err) } } diff --git a/platform/service/lifecycle_defaults.go b/platform/service/lifecycle_defaults.go index 7a24c58..d939f6f 100644 --- a/platform/service/lifecycle_defaults.go +++ b/platform/service/lifecycle_defaults.go @@ -4,6 +4,9 @@ import "browser.local/platform/domain" func applyPluginCreateDefaults(plugin domain.GamePlugin, definition domain.ServerDeploymentDefinition) domain.ServerDeploymentDefinition { definition = domain.CopyServerDeploymentDefinition(definition) + if definition.ProfileKey == "" && len(plugin.RuntimeProfiles.LifecycleProfiles) > 0 { + definition.ProfileKey = plugin.RuntimeProfiles.LifecycleProfiles[0].Key + } if definition.Mode != domain.ServerDeploymentModeGuided { return definition } diff --git a/platform/service/server_deployment.go b/platform/service/server_deployment.go index 051be89..f589799 100644 --- a/platform/service/server_deployment.go +++ b/platform/service/server_deployment.go @@ -60,10 +60,7 @@ func (svc *CoreService) UpdateServerDeploymentForSession(sessionID, serverInstan return domain.ServerDeploymentView{}, err } if update.RunEndpointID != "" { - if _, err := svc.store.RunEndpoints().Get(update.RunEndpointID); err != nil { - return domain.ServerDeploymentView{}, err - } - instance.RunEndpointID = update.RunEndpointID + return domain.ServerDeploymentView{}, validationError("run endpoint identity is managed by Run heartbeat") } instance.Deployment = definition instance.DeploymentProjection = domain.ServerDeploymentProjection{} @@ -105,12 +102,9 @@ func (svc *CoreService) deployServerInstance(command domain.ServerLifecycleComma return domain.ServerLifecycleResult{}, validationError("deployment definition is required") } if strings.TrimSpace(instance.RunEndpointID) == "" { - return domain.ServerLifecycleResult{}, validationError("run endpoint must be selected before deployment") + return domain.ServerLifecycleResult{}, validationError("an active Run heartbeat is required for legacy manual deployment dispatch") } if _, err := svc.store.RunEndpoints().Get(instance.RunEndpointID); err != nil { - if errors.Is(err, repo.ErrNotFound) && strings.TrimSpace(instance.DeploymentTargetID) != "" { - return domain.ServerLifecycleResult{}, validationError("dedicated Run must register before deployment") - } return domain.ServerLifecycleResult{}, err } plugin, endpoint, err := svc.lifecycleDependencies(instance.PluginID, instance.RunEndpointID) diff --git a/platform/service/server_deployment_test.go b/platform/service/server_deployment_test.go index cb93e3d..f63b8b5 100644 --- a/platform/service/server_deployment_test.go +++ b/platform/service/server_deployment_test.go @@ -7,7 +7,7 @@ import ( "browser.local/platform/domain" ) -func TestCoreServiceSavesDraftDeploymentRedactsReadsAndDispatchesOnlyToCompatibleRun(t *testing.T) { +func TestCoreServiceSavesDraftDeploymentRedactsReadsAndKeepsRunIdentityHeartbeatManaged(t *testing.T) { svc, _ := newLifecycleRunService(t) createLifecyclePlugin(t, svc) ownerSession := createServiceUserAndLogin(t, svc, domain.User{ID: "deployment-owner", DisplayName: "Deployment Owner", Email: "deployment-owner@example.test", Roles: []string{"server-owner"}, PasswordHash: "secret-password"}) @@ -37,31 +37,8 @@ func TestCoreServiceSavesDraftDeploymentRedactsReadsAndDispatchesOnlyToCompatibl t.Fatalf("expected explicit deployment reveal, reveal=%+v err=%v", revealed, err) } - if _, err := svc.UpdateServerDeploymentForSession(ownerSession, draft.Instance.ID, domain.ServerDeploymentUpdate{RunEndpointID: "run-local", Mode: domain.ServerDeploymentModeCustom}); err != nil { - t.Fatalf("bind draft to run: %v", err) - } - if _, err := svc.DeployServerInstanceForSession(ownerSession, domain.ServerLifecycleCommand{ServerInstanceID: draft.Instance.ID, ExpectedConfigVersion: draft.Instance.ConfigVersion, IdempotencyKey: "deploy-incompatible"}); err == nil || !strings.Contains(err.Error(), "deployment.plan.v1") { - t.Fatalf("expected incompatible Run rejection, got %v", err) - } - - endpoint, err := svc.store.RunEndpoints().Get("run-local") - if err != nil { - t.Fatalf("get endpoint: %v", err) - } - endpoint.Capabilities = append(endpoint.Capabilities, domain.JobCapabilityDeploymentPlan) - if err := svc.store.RunEndpoints().Update(endpoint); err != nil { - t.Fatalf("enable deployment capability: %v", err) - } - deployed, err := svc.DeployServerInstanceForSession(ownerSession, domain.ServerLifecycleCommand{ServerInstanceID: draft.Instance.ID, ExpectedConfigVersion: draft.Instance.ConfigVersion, IdempotencyKey: "deploy-compatible"}) - if err != nil { - t.Fatalf("deploy compatible draft: %v", err) - } - if deployed.Job.ExecutionInput.Deployment == nil || deployed.Job.ExecutionInput.Deployment.StartCommand != "/srv/venv-server/.venv/bin/python server.py" || deployed.Job.Progress.Phase != "queued" { - t.Fatalf("Run job must carry protected plan and queued phase: %+v", deployed.Job) - } - view, err = svc.GetServerDeploymentForSession(ownerSession, draft.Instance.ID) - if err != nil || view.LatestDispatch == nil || view.LatestDispatch.JobID != deployed.Job.ID || view.LatestDispatch.DeploymentRevision != deployed.Job.ExecutionInput.Deployment.Revision || !view.LatestDispatch.DeploymentDefinitionIncluded { - t.Fatalf("expected safe dispatch evidence, view=%+v err=%v", view, err) + if _, err := svc.UpdateServerDeploymentForSession(ownerSession, draft.Instance.ID, domain.ServerDeploymentUpdate{RunEndpointID: "run-local", Mode: domain.ServerDeploymentModeCustom}); err == nil || !strings.Contains(err.Error(), "managed by Run heartbeat") { + t.Fatalf("expected Run identity update to be rejected, got %v", err) } } diff --git a/platform/service/server_lifecycle.go b/platform/service/server_lifecycle.go index b2f4a83..cd54cd2 100644 --- a/platform/service/server_lifecycle.go +++ b/platform/service/server_lifecycle.go @@ -152,10 +152,24 @@ func (svc *CoreService) CreateServerInstanceWorkflow(create domain.ServerLifecyc }), nil } -func dedicatedRunEndpointID(serverInstanceID string) string { +func generatedRunEndpointID(serverInstanceID string) string { return "server-run-" + serverInstanceID } +// dedicatedRunEndpointID remains a compatibility helper for legacy fixtures. +// Production flow uses generatedRunEndpointID only when building a package and +// attaches the active endpoint from the first authenticated Run heartbeat. +func dedicatedRunEndpointID(serverInstanceID string) string { + return generatedRunEndpointID(serverInstanceID) +} + +func runEndpointIDForDistribution(instance domain.ServerInstance) string { + if endpointID := strings.TrimSpace(instance.RunEndpointID); endpointID != "" { + return endpointID + } + return generatedRunEndpointID(instance.ID) +} + func (svc *CoreService) CreateServerInstanceWorkflowForSession(sessionID string, create domain.ServerLifecycleCreate) (domain.ServerLifecycleResult, error) { user, err := svc.GetCurrentUser(sessionID) if err != nil { diff --git a/platform/service/server_lifecycle_test.go b/platform/service/server_lifecycle_test.go index 64aebf3..a36e68a 100644 --- a/platform/service/server_lifecycle_test.go +++ b/platform/service/server_lifecycle_test.go @@ -159,43 +159,36 @@ func assertLogProcessStateEvent(t *testing.T, subscription LogEventSubscription, } } -func TestCoreServiceCreatesTargetBoundDraftAndRequiresDedicatedRunRegistration(t *testing.T) { +func TestCoreServiceCreatesUnboundDraftAndAttachesRunFromHeartbeat(t *testing.T) { svc, _ := newLifecycleRunService(t) plugin := createLifecyclePlugin(t, svc) - draft, err := svc.CreateServerInstanceWorkflow(domain.ServerLifecycleCreate{ - ID: "server-dedicated", PluginID: plugin.ID, DeploymentTargetID: "run-local", Name: "Dedicated SCUM", IdempotencyKey: "dedicated-draft", ProfileKey: "local", - }) + draft, err := svc.CreateServerInstanceWorkflow(domain.ServerLifecycleCreate{ID: "server-dedicated", PluginID: plugin.ID, Name: "Dedicated SCUM", IdempotencyKey: "dedicated-draft"}) if err != nil { t.Fatalf("create target-bound draft: %v", err) } - if draft.Instance.State != domain.ServerInstanceStateDraft || draft.Job.ID != "" || draft.Instance.DeploymentTargetID != "run-local" || draft.Instance.RunEndpointID != "server-run-server-dedicated" { - t.Fatalf("expected draft with separate target and reserved Run identity, got %+v", draft) - } - if _, err := svc.DeployServerInstanceForSession("", domain.ServerLifecycleCommand{ServerInstanceID: draft.Instance.ID, ExpectedConfigVersion: draft.Instance.ConfigVersion, IdempotencyKey: "before-register"}); err == nil { - t.Fatal("expected deployment without a registered dedicated Run to fail") + if draft.Instance.State != domain.ServerInstanceStateDraft || draft.Job.ID != "" || draft.Instance.RunEndpointID != "" { + t.Fatalf("expected draft without a reserved Run identity, got %+v", draft) } key, plainKey, err := svc.ensureActiveComponentKey(draft.Instance.ID, domain.DistributionComponentRun, "") if err != nil { t.Fatalf("create Run key: %v", err) } - wrong := validRunControlHello() - wrong.ServerInstanceID = draft.Instance.ID - wrong.PluginID = plugin.ID - wrong.ComponentKind = domain.DistributionComponentRun - wrong.KeyGeneration = key.Generation - wrong.RegistrationToken = plainKey - wrong.RunEndpointID = "run-local" - if _, err := svc.RegisterRunHello(wrong); err == nil || !strings.Contains(err.Error(), "does not match") { - t.Fatalf("expected mismatched endpoint registration rejection, got %v", err) + hello := validRunControlHello() + hello.ServerInstanceID = draft.Instance.ID + hello.PluginID = plugin.ID + hello.ComponentKind = domain.DistributionComponentRun + hello.KeyGeneration = key.Generation + hello.RegistrationToken = plainKey + hello.RunEndpointID = "run-local" + hello.DisplayName = "Automatic SCUM Run" + if registered, err := svc.RegisterRunHello(hello); err != nil || !registered.Accepted { + t.Fatalf("register automatic Run heartbeat: result=%+v err=%v", registered, err) } - - correct := wrong - correct.RunEndpointID = draft.Instance.RunEndpointID - correct.DisplayName = "Dedicated SCUM Run" - if registered, err := svc.RegisterRunHello(correct); err != nil || !registered.Accepted { - t.Fatalf("register dedicated Run: result=%+v err=%v", registered, err) + attached, err := svc.GetServerInstance(draft.Instance.ID) + if err != nil || attached.RunEndpointID != "run-local" { + t.Fatalf("expected heartbeat to attach Run endpoint, instance=%+v err=%v", attached, err) } } diff --git a/platform_web/api/contracts.md b/platform_web/api/contracts.md index ef9e6b7..eded2e8 100644 --- a/platform_web/api/contracts.md +++ b/platform_web/api/contracts.md @@ -22,7 +22,7 @@ Normal browser login uses the platform's HttpOnly SameSite cookie and `credentia ## Server Management Workflows -- `createServerWorkflow` posts `ServerLifecycleCreateRequest` with the create-wizard deployment definition to `/server-instances/workflows/create`, including deployment mode, plugin create inputs, and custom startup fields when provided. It must not include deployment target, run endpoint, runtime profile, or runtime bindings during creation; those are established only after creation through generated Run registration, runtime binding, or deployment update flows. +- `createServerWorkflow` posts `ServerLifecycleCreateRequest` with the create-wizard deployment definition to `/server-instances/workflows/create`, including deployment mode, plugin create inputs, and custom startup fields when provided. It never includes a deployment target, Run endpoint, lifecycle profile, or Run identity binding. The generated Run package uses plugin defaults and Platform observes the active Run from its authenticated heartbeat. - `getServerRuntimeBinding` reads `/server-instances/{id}/runtime-binding`; `updateServerRuntimeBinding` patches the selected profile and logical refs for internal/advanced logical transports. Server detail must not expose a manual runtime-binding tab or require these fields before normal start/stop when plugin-declared deployment/lifecycle data is sufficient. Responses contain only profile metadata, logical key names, configured/secret-backed flags, missing keys, and safe reasons. They never contain stored refs or secret values. - `startServerInstance` and `stopServerInstance` post `ServerLifecycleCommandRequest` with the current config version and receive the lifecycle job response. - `listServerAdministratorCandidates`, `addServerAdministrator`, and `removeServerAdministrator` call server membership endpoints so server owners can invite or remove active non-platform-admin server administrators. @@ -38,7 +38,7 @@ Normal browser login uses the platform's HttpOnly SameSite cookie and `credentia - `listMetricHistory`, `listBackups`, and `getBackup` read bounded owner-scoped metric and backup projections. Backup responses contain artifact IDs/checksums and recovery/retention state only; they never include body bytes or storage paths. - `listRemoteAdapters` and `requestRemoteAdapter` use declaration-backed logical target keys and return queued status/result references. The browser never receives adapter credentials, host addresses, sockets, Run tokens, leases, session hashes, or secret refs. - Server management DTOs may include bounded `ownerUserId` and `adminUserIds` metadata, but must not include raw run credentials, host paths, direct socket details, user password hashes, or AI provider keys. -- Server creation and detail forms derive profile choices and binding fields from `GamePluginResponse.runtimeProfiles`; they must not hardcode a complete state or game-specific machine paths. +- Server creation and detail forms use plugin-declared deployment inputs only; lifecycle profile selection, Run identity binding, and Run registration waits are not operator controls. Platform applies the plugin default and observes the active Run from authenticated heartbeats. - AI provider responses expose `apiKeyConfigured` only. Existing secret refs are never rehydrated into edit forms; a blank update preserves the platform-owned secret reference. ## Redesign Contract Gaps (redesign-platform-web-interactions) diff --git a/platform_web/api/types.ts b/platform_web/api/types.ts index 1290a58..0844f7d 100644 --- a/platform_web/api/types.ts +++ b/platform_web/api/types.ts @@ -461,7 +461,6 @@ export interface ServerInstanceResponse { id: string; pluginId: string; pluginVersion: string; - deploymentTargetId?: string; runEndpointId: string; name: string; ownerUserId?: string; @@ -522,10 +521,7 @@ export type ServerCommandShell = "" | "posix-sh" | "powershell" | "cmd"; // This request is write-only for paths and commands. The matching response // intentionally returns configured flags rather than those values. export interface ServerDeploymentRequest { - runEndpointId?: string; mode: ServerDeploymentMode; - profileKey?: string; - runtimeBindings?: Record; createInputs?: Record; serverRoot?: string; workingDirectory?: string; @@ -539,7 +535,6 @@ export interface ServerDeploymentRequest { export interface ServerDeploymentResponse { serverInstanceId: string; mode?: ServerDeploymentMode; - profileKey?: string; createInputs?: Record; serverRootConfigured: boolean; workingDirectoryConfigured: boolean; diff --git a/platform_web/components/ServerDeploymentWorkflow.test.tsx b/platform_web/components/ServerDeploymentWorkflow.test.tsx index 42bee22..74ff3d5 100644 --- a/platform_web/components/ServerDeploymentWorkflow.test.tsx +++ b/platform_web/components/ServerDeploymentWorkflow.test.tsx @@ -52,7 +52,7 @@ describe("ServerDeploymentWorkflow", () => { container = document.createElement("div"); document.body.append(container); root = createRoot(container); - const initialForm = defaultServerCreateForm([plugin], []); + const initialForm = defaultServerCreateForm([plugin]); let submitted: ReturnType | undefined; const onSubmit = vi.fn(async (form: typeof initialForm) => { submitted = serverCreateRequestFromForm(form, 17); @@ -64,7 +64,6 @@ describe("ServerDeploymentWorkflow", () => { open kind="create" plugins={[plugin]} - endpoints={[]} initialForm={initialForm} onClose={() => undefined} onSubmit={onSubmit} diff --git a/platform_web/components/ServerDeploymentWorkflow.tsx b/platform_web/components/ServerDeploymentWorkflow.tsx index f5d4c6a..9e6b80e 100644 --- a/platform_web/components/ServerDeploymentWorkflow.tsx +++ b/platform_web/components/ServerDeploymentWorkflow.tsx @@ -1,9 +1,9 @@ import { CheckCircle2, CircleDashed, Compass, Download, FolderCog, HeartPulse, Rocket, ScanSearch, ServerCog, SlidersHorizontal } from "lucide-react"; import { type ChangeEvent, type FormEvent, useEffect, useMemo, useState } from "react"; -import type { GamePluginResponse, RunEndpointResponse, ServerDeploymentResponse, ServerDeploymentRevealResponse } from "../api/types"; +import type { GamePluginResponse, ServerDeploymentResponse, ServerDeploymentRevealResponse } from "../api/types"; import { ManagementDialog } from "./OperationControls"; -import { endpointLabel, pluginCreateInputDefaults, pluginLabel, type ServerCreateFormState } from "../contracts/serverManagement"; +import { pluginCreateInputDefaults, pluginLabel, type ServerCreateFormState } from "../contracts/serverManagement"; import { cx } from "../utils/classes"; type WorkflowKind = "create" | "edit"; @@ -12,7 +12,6 @@ interface ServerDeploymentWorkflowProps { open: boolean; kind: WorkflowKind; plugins: GamePluginResponse[]; - endpoints: RunEndpointResponse[]; initialForm: ServerCreateFormState; deployment?: ServerDeploymentResponse; busy?: boolean; @@ -21,7 +20,7 @@ interface ServerDeploymentWorkflowProps { onSubmit: (form: ServerCreateFormState) => Promise; } -export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initialForm, deployment, busy = false, onReveal, onClose, onSubmit }: ServerDeploymentWorkflowProps) { +export function ServerDeploymentWorkflow({ open, kind, plugins, initialForm, deployment, busy = false, onReveal, onClose, onSubmit }: ServerDeploymentWorkflowProps) { const [step, setStep] = useState(0); const [form, setForm] = useState(initialForm); const [revealBusy, setRevealBusy] = useState(false); @@ -29,17 +28,12 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initi const selectedPlugin = useMemo(() => plugins.find((plugin) => plugin.id === form.pluginId), [form.pluginId, plugins]); const pluginFields = selectedPlugin?.createFields ?? []; const isScum = selectedPlugin?.id === "game.scum"; - const needsTargetSelection = kind === "edit" && !initialForm.runEndpointId; - const selectedTargetID = form.runEndpointId; const workflowSteps = kind === "create" ? [{ label: "基本信息", icon: Compass }, { label: "部署方式", icon: ServerCog }, { label: "相关配置", icon: FolderCog }, { label: "确认", icon: Rocket }] - : needsTargetSelection - ? [{ label: "选择运行节点", icon: Compass }, { label: "相关配置", icon: FolderCog }, { label: "确认", icon: Rocket }] - : [{ label: "相关配置", icon: FolderCog }, { label: "确认", icon: Rocket }]; + : [{ label: "相关配置", icon: FolderCog }, { label: "确认", icon: Rocket }]; const pluginStep = kind === "create" ? 0 : -1; - const targetStep = needsTargetSelection ? 0 : -1; const modeStep = kind === "create" ? 1 : -1; - const configurationStep = kind === "create" ? 2 : needsTargetSelection ? 1 : 0; + const configurationStep = kind === "create" ? 2 : 0; const reviewStep = workflowSteps.length - 1; useEffect(() => { @@ -60,9 +54,8 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initi setForm((current) => { if (name === "pluginId") { const plugin = plugins.find((item) => item.id === value); - return { ...current, pluginId: value, profileKey: plugin?.runtimeProfiles?.lifecycleProfiles?.[0]?.key ?? "", bindings: {}, createInputs: pluginCreateInputDefaults(plugin) }; + return { ...current, pluginId: value, createInputs: pluginCreateInputDefaults(plugin) }; } - if (name === "profileKey") return { ...current, profileKey: value, bindings: {} }; return { ...current, [name]: value }; }); } @@ -72,7 +65,6 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initi function canContinue() { if (step === pluginStep) return Boolean(form.pluginId) && Boolean(form.name.trim()); if (step === modeStep) return Boolean(form.deploymentMode); - if (step === targetStep) return Boolean(form.runEndpointId); if (step === configurationStep) { if (isScum && form.deploymentMode === "guided-install" && !form.serverRoot.trim() && !deployment?.serverRootConfigured) return false; if (form.deploymentMode === "existing-server" && !form.serverRoot.trim() && !deployment?.serverRootConfigured) return false; @@ -112,24 +104,20 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initi const protectedState = (nextValue: string, configured: boolean) => nextValue.trim() ? "将替换" : configured ? "保持已配置" : "未配置"; const actionLabel = kind === "create" ? "创建服务器" : "保存部署设置"; - return + return
void submit(event)} aria-label={kind === "create" ? "创建服务器部署向导" : "编辑服务器部署向导"}>
    {workflowSteps.map((item, index) => { const Icon = item.icon; return
  1. {index < step ? : }{index + 1}. {item.label}
  2. ; })}
{step === pluginStep &&
-
创建基础信息插件决定下一步显示哪些部署方式和游戏参数。
配置启动项新建安装、接管已有和自定义启动分别填写自己的字段。
平台构建专属 Run平台在自有构建器中打包,不需要你先选择部署目标。
+
创建基础信息插件决定下一步显示哪些部署方式和游戏参数。
配置启动项新建安装、接管已有和自定义启动分别填写自己的字段。
平台构建 Run 包平台在自有构建器中打包,Run 启动后自动上报心跳。
} {step === modeStep &&

选择这台服务器的创建方式;下一步只显示该方式需要的启动项。

{isScum &&
SCUM 受控链路Run 会按预检 → 安装或扫描 → 配置映射 → 健康验证执行;目录本身不代表安装完成。
}
setForm((current) => ({ ...current, deploymentMode: "guided-install" }))} /> setForm((current) => ({ ...current, deploymentMode: "existing-server" }))} /> - setForm((current) => ({ ...current, deploymentMode: "custom-command" }))} /> + setForm((current) => ({ ...current, deploymentMode: "custom-command" }))} />
} - {step === targetStep &&
-
这个草稿尚未绑定运行节点只需在这里补选一次。已绑定服务器编辑时会直接进入相关配置,不会重复要求选择目标。
-
-
} {step === configurationStep &&
{kind === "edit" && onReveal &&
已读取受保护配置{revealBusy ? "正在读取已保存的目录和命令…" : "这些值只保留在当前编辑窗口,关闭后会清除。"}{revealError && <>{revealError}}
}
- {kind === "edit" && } + {kind === "edit" && } {form.deploymentMode === "guided-install" && } {form.deploymentMode === "existing-server" && } {form.deploymentMode === "custom-command" && } @@ -143,9 +131,9 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initi
{form.deploymentMode === "guided-install" && } {form.deploymentMode === "existing-server" && } - {form.deploymentMode === "custom-command" &&
高级启动设置

只有自定义启动器需要这些设置。执行目录留空时,节点以服务器目录执行。

} + {form.deploymentMode === "custom-command" &&
高级启动设置

只有自定义启动器需要这些设置。执行目录留空时,Run 以服务器目录执行。

}
} - {step === reviewStep && (kind === "create" ?
插件类型{pluginLabel(selectedPlugin, form.pluginId)}
服务器名称{form.name.trim() || "未填写"}
部署方式{form.deploymentMode === "guided-install" ? "新建并安装" : form.deploymentMode === "existing-server" ? "接管已有服务器" : "自定义启动方式"}
{form.deploymentMode === "guided-install" ? "安装目录" : form.deploymentMode === "existing-server" ? "已有服务器目录" : "服务器目录"}{protectedState(form.serverRoot, false)}
{form.deploymentMode === "custom-command" && <>
启动命令{protectedState(form.startCommand, false)}
执行目录{protectedState(form.workingDirectory, false)}
}{form.deploymentMode === "guided-install" &&
游戏配置{Object.keys(form.createInputs).length ? `${Object.keys(form.createInputs).length} 项已准备` : "使用插件默认值"}
}{isScum &&
完成条件安装/扫描、映射、验证全部通过
}
本次保存创建向导配置保存后生成并启动专属 Run;部署执行会按这里选择的方式和启动项进行。
:
目标{endpointLabel(endpoints.find((endpoint) => endpoint.id === selectedTargetID), selectedTargetID)}
部署方式{form.deploymentMode === "guided-install" ? "新建并安装" : form.deploymentMode === "existing-server" ? "接管已有服务器" : "自定义启动方式"}
{form.deploymentMode === "guided-install" ? "安装目录" : form.deploymentMode === "existing-server" ? "已有服务器目录" : "服务器目录"}{protectedState(form.serverRoot, Boolean(deployment?.serverRootConfigured))}
{form.deploymentMode === "custom-command" && <>
启动命令{protectedState(form.startCommand, Boolean(deployment?.startCommandConfigured))}
执行目录{protectedState(form.workingDirectory, Boolean(deployment?.workingDirectoryConfigured))}
}{form.deploymentMode === "guided-install" &&
游戏配置{Object.keys(form.createInputs).length ? `${Object.keys(form.createInputs).length} 项已准备` : "使用插件默认值"}
}{isScum &&
完成条件安装/扫描、映射、验证全部通过
}
本次只保存部署设置{form.deploymentMode === "existing-server" ? "Run 将先预检现有目录;不会重装或覆盖已有游戏配置。" : "保存后由平台保留受保护部署设置;路径和命令仅在本次显式展示后可见。"}
)} + {step === reviewStep &&
插件类型{pluginLabel(selectedPlugin, form.pluginId)}
{kind === "create" &&
服务器名称{form.name.trim() || "未填写"}
}
部署方式{form.deploymentMode === "guided-install" ? "新建并安装" : form.deploymentMode === "existing-server" ? "接管已有服务器" : "自定义启动方式"}
{form.deploymentMode === "guided-install" ? "安装目录" : form.deploymentMode === "existing-server" ? "已有服务器目录" : "服务器目录"}{protectedState(form.serverRoot, Boolean(deployment?.serverRootConfigured))}
{form.deploymentMode === "custom-command" && <>
启动命令{protectedState(form.startCommand, Boolean(deployment?.startCommandConfigured))}
执行目录{protectedState(form.workingDirectory, Boolean(deployment?.workingDirectoryConfigured))}
}{form.deploymentMode === "guided-install" &&
游戏配置{Object.keys(form.createInputs).length ? `${Object.keys(form.createInputs).length} 项已准备` : "使用插件默认值"}
}{isScum &&
完成条件安装/扫描、映射、验证全部通过
}
{kind === "create" ? "本次保存创建向导配置" : "本次只保存部署设置"}{kind === "create" ? "Run 会自动识别并上报心跳;部署执行按这里的方式和启动项进行。" : form.deploymentMode === "existing-server" ? "Run 将自动识别并预检现有目录;不会重装或覆盖已有游戏配置。" : "保存后由平台保留受保护部署设置;路径和命令仅在本次显式展示后可见。"}
}
{step < reviewStep ? : }
; @@ -155,12 +143,12 @@ function ModeOption({ active, title, copy, onClick }: { active: boolean; title: function GuidedInstallPlan({ pluginName, isScum }: { pluginName: string; isScum: boolean }) { const steps = isScum ? [ - { icon: ScanSearch, title: "预检目录与端口", copy: "确认安装目录可用、节点兼容且端口可绑定。" }, + { icon: ScanSearch, title: "预检目录与端口", copy: "确认安装目录可用、Run 环境兼容且端口可用。" }, { icon: Download, title: "下载 SCUM Server", copy: "通过 SteamCMD 安装 App 3792580 到该目录。" }, { icon: SlidersHorizontal, title: "写入游戏配置", copy: "把本页的名称、端口与人数写入 ServerSettings.ini。" }, { icon: HeartPulse, title: "启动并健康验证", copy: "检查可执行文件、版本、配置、端口和服务进程。" } ] : [ - { icon: ScanSearch, title: "预检目录与节点", copy: "确认安装目录、权限、端口与运行节点可用。" }, + { icon: ScanSearch, title: "预检目录与 Run", copy: "确认安装目录、权限、端口与 Run 环境可用。" }, { icon: Download, title: "安装游戏服务端", copy: "按插件声明的推荐方案安装到该目录。" }, { icon: SlidersHorizontal, title: "写入游戏配置", copy: "将本页填写的游戏参数交给受控部署流程。" }, { icon: HeartPulse, title: "启动并健康验证", copy: "只有启动与插件要求的验证通过才会显示成功。" } @@ -174,15 +162,15 @@ function ExistingServerAdoptionPlan({ pluginName, isScum }: { pluginName: string { icon: FolderCog, title: "定位服务端根目录", copy: "填写包含 SCUM 服务端文件、数据与配置的目录,不是 Steam 库或 SteamCMD 目录。" }, { icon: ScanSearch, title: "Run 本机预检", copy: "检查目录权限、可执行文件、版本、Steam App 标记和所需端口。" }, { icon: SlidersHorizontal, title: "只读扫描配置", copy: "识别 ServerSettings.ini 与现有参数;接管不会写入或覆盖它们。" }, - { icon: ServerCog, title: "绑定受控生命周期", copy: "记录这台实例由哪个 Run 管理,后续启动、停止和日志仍走受控通道。" }, + { icon: ServerCog, title: "建立受控生命周期", copy: "Run 自动识别这台实例,后续启动、停止和日志仍走受控通道。" }, { icon: HeartPulse, title: "健康验证", copy: "确认端口、进程与配置可读后,才标记为接管成功。" } ] : [ { icon: FolderCog, title: "定位服务端根目录", copy: "填写已有服务端文件、数据与配置所在的主目录。" }, { icon: ScanSearch, title: "Run 本机预检", copy: "检查目录权限、插件识别和端口是否可用。" }, { icon: SlidersHorizontal, title: "只读扫描配置", copy: "读取插件需要的现有状态,不把新建默认值写进服务器。" }, - { icon: ServerCog, title: "绑定受控生命周期", copy: "后续运行操作由绑定的 Run 通过平台通道执行。" }, + { icon: ServerCog, title: "建立受控生命周期", copy: "后续运行操作由自动识别的 Run 通过平台通道执行。" }, { icon: HeartPulse, title: "健康验证", copy: "验证通过后才标记为接管成功。" } ]; - return
确认后,{pluginName} 会这样接管目录只会交给目标 Run 在本机使用;平台、浏览器和日志都不会显示原始路径。
先扫描,后绑定
    {steps.map(({ icon: Icon, title, copy }, index) =>
  1. {index + 1}. {title}{copy}
  2. )}
{isScum ?

SCUM 与 SteamCMD:接管只需要服务端根目录,不需要填写 SteamCMD 目录。Run 可能按本机策略检查 SteamCMD 是否可用,但它不是接管输入。
升级:接管不会升级游戏;当前平台尚未提供 SCUM 服务端的受控升级任务,不能承诺自动升级。升级能力需要单独的 SteamCMD 更新任务与备份/健康验证流程。

:

不会做:不会重新安装、覆盖已有游戏配置,或把受保护路径回显给浏览器。

}
; + return
确认后,{pluginName} 会这样接管目录只会交给 Run 在本机使用;平台、浏览器和日志都不会显示原始路径。
先扫描,后自动识别
    {steps.map(({ icon: Icon, title, copy }, index) =>
  1. {index + 1}. {title}{copy}
  2. )}
{isScum ?

SCUM 与 SteamCMD:接管只需要服务端根目录,不需要填写 SteamCMD 目录。Run 可能按本机策略检查 SteamCMD 是否可用,但它不是接管输入。
升级:接管不会升级游戏;当前平台尚未提供 SCUM 服务端的受控升级任务,不能承诺自动升级。升级能力需要单独的 SteamCMD 更新任务与备份/健康验证流程。

:

不会做:不会重新安装、覆盖已有游戏配置,或把受保护路径回显给浏览器。

}
; } diff --git a/platform_web/contracts/serverManagement.ts b/platform_web/contracts/serverManagement.ts index b21f91b..72083e9 100644 --- a/platform_web/contracts/serverManagement.ts +++ b/platform_web/contracts/serverManagement.ts @@ -15,10 +15,6 @@ export interface ServerCreateFormState { id: string; name: string; pluginId: string; - deploymentTargetId: string; - runEndpointId: string; - profileKey: string; - bindings: Record; createInputs: Record; deploymentMode: ServerDeploymentMode; serverRoot: string; @@ -30,12 +26,6 @@ export interface ServerCreateFormState { shell: "" | "posix-sh" | "powershell" | "cmd"; } -export interface RuntimeBindingField { - key: string; - required: boolean; - sensitive: boolean; -} - export interface ServerWorkflowActionState { label: ServerLifecycleActionLabel; serverInstanceId?: string; @@ -73,10 +63,6 @@ export const emptyServerCreateForm: ServerCreateFormState = { id: "", name: "", pluginId: "", - deploymentTargetId: "", - runEndpointId: "", - profileKey: "", - bindings: {}, createInputs: {}, deploymentMode: "guided-install", serverRoot: "", @@ -108,13 +94,6 @@ export function pluginLabel(plugin: GamePluginResponse | undefined, pluginId: st return plugin.serverDisplayName || plugin.name || plugin.id; } -export function endpointLabel(endpoint: RunEndpointResponse | undefined, runEndpointId: string): string { - if (!endpoint) { - return runEndpointId; - } - return endpoint.displayName || endpoint.id; -} - export function canStartServer(state: ServerInstanceState): boolean { return state === "ready" || state === "stopped" || state === "failed"; } @@ -127,13 +106,11 @@ export function isPendingJobState(state: JobResponse["state"]): boolean { return state === "queued" || state === "accepted" || state === "running" || state === "retrying"; } -export function defaultServerCreateForm(plugins: GamePluginResponse[], endpoints: RunEndpointResponse[]): ServerCreateFormState { +export function defaultServerCreateForm(plugins: GamePluginResponse[]): ServerCreateFormState { const plugin = plugins[0]; return { ...emptyServerCreateForm, pluginId: plugin?.id ?? "", - profileKey: plugin?.runtimeProfiles?.lifecycleProfiles?.[0]?.key ?? "", - runEndpointId: "", createInputs: pluginCreateInputDefaults(plugin) }; } @@ -142,30 +119,6 @@ export function pluginCreateInputDefaults(plugin: GamePluginResponse | undefined return Object.fromEntries((plugin?.createFields ?? []).map((field) => [field.key, field.defaultValue ?? ""])); } -export function runtimeBindingFields(plugin: GamePluginResponse | undefined, profileKey: string): RuntimeBindingField[] { - const profiles = plugin?.runtimeProfiles; - const lifecycle = profiles?.lifecycleProfiles?.find((profile) => profile.key === profileKey); - if (!profiles || !lifecycle) return []; - const fields = new Map(); - const add = (key: string | undefined, required: boolean) => { - if (!key) return; - const current = fields.get(key); - fields.set(key, { key, required: required || current?.required === true, sensitive: runtimeBindingKeyIsSensitive(key) }); - }; - profiles.discovery?.forEach((probe) => add(probe.targetKey, probe.required === true)); - profiles.dependencyProbes?.forEach((probe) => add(probe.targetKey, probe.required === true)); - profiles.logSources?.forEach((source) => add(source.targetKey, Boolean(source.targetKey))); - profiles.installPlans?.forEach((plan) => plan.steps.forEach((step) => add(step.targetKey, false))); - profiles.transportProfiles?.filter((transport) => lifecycle.transportKeys?.includes(transport.key)).forEach((transport) => add(transport.targetKey || transport.key, true)); - add(lifecycle.clientManagerRef, Boolean(lifecycle.clientManagerRef)); - return [...fields.values()].sort((left, right) => left.key.localeCompare(right.key)); -} - -export function runtimeBindingKeyIsSensitive(key: string): boolean { - const normalized = key.toLowerCase(); - return ["password", "credential", "secret", "token", "dsn"].some((part) => normalized.includes(part)); -} - export function serverMetadataFormFromInstance(instance: ServerInstanceResponse): ServerMetadataFormState { return { name: instance.name }; } diff --git a/platform_web/pages/ConsolePages.test.tsx b/platform_web/pages/ConsolePages.test.tsx index ba29018..edbc47e 100644 --- a/platform_web/pages/ConsolePages.test.tsx +++ b/platform_web/pages/ConsolePages.test.tsx @@ -183,7 +183,7 @@ describe("first-party console pages", () => { expect(serverDeploymentWorkflowSource).toContain("基本信息"); expect(serverDeploymentWorkflowSource).toContain("部署方式"); expect(serverDeploymentWorkflowSource).toContain("相关配置"); - expect(serverDeploymentWorkflowSource).toContain("专属 Run"); + expect(serverDeploymentWorkflowSource).toContain("自动上报心跳"); expect(serverDeploymentWorkflowSource).toContain("创建服务器"); expect(serverDeploymentWorkflowSource).toContain("执行目录(可选)"); expect(serverDeploymentWorkflowSource).toContain("默认使用服务器目录"); @@ -197,8 +197,7 @@ describe("first-party console pages", () => { expect(serverDeploymentWorkflowSource).toContain("接管已有服务器执行流程"); expect(serverDeploymentWorkflowSource).toContain("不需要填写 SteamCMD 目录"); expect(serverDeploymentWorkflowSource).toContain("当前平台尚未提供 SCUM 服务端的受控升级任务"); - expect(serverDeploymentWorkflowSource).toContain("已绑定服务器编辑时会直接进入相关配置"); - expect(serverDeploymentWorkflowSource).toContain("可在此调整部署方式;不会重复要求选择已绑定的运行节点"); + expect(serverDeploymentWorkflowSource).toContain("Run 会按心跳自动识别服务器"); expect(serversPageSource).toContain('onNavigate("serverDetail", { serverId: result.instance.id })'); expect(serverDetailPageSource).not.toContain("运行配置绑定"); expect(serverDetailPageSource).not.toContain('type={field.sensitive ? "password" : "text"}'); @@ -252,8 +251,10 @@ describe("first-party console pages", () => { expect(serverDeploymentWorkflowSource).toContain("配置启动项"); expect(serverDeploymentWorkflowSource).toContain("本次保存创建向导配置"); expect(serverDeploymentWorkflowSource).toContain('const modeStep = kind === "create" ? 1 : -1;'); - expect(serverDeploymentWorkflowSource).toContain('const configurationStep = kind === "create" ? 2 : needsTargetSelection ? 1 : 0;'); - expect(serverDeploymentWorkflowSource).toContain('const needsTargetSelection = kind === "edit" && !initialForm.runEndpointId;'); + expect(serverDeploymentWorkflowSource).toContain('const configurationStep = kind === "create" ? 2 : 0;'); + expect(serverDeploymentWorkflowSource).not.toContain("needsTargetSelection"); + expect(serverDeploymentWorkflowSource).not.toContain('name="runEndpointId"'); + expect(serverDeploymentWorkflowSource).not.toContain("profileKey"); expect(serversPageSource).toContain("serverCreateRequestFromForm(nextForm)"); expect(serverCreateSchemaSource).not.toContain("runEndpointId: form.runEndpointId"); expect(serverCreateSchemaSource).not.toContain("deploymentTargetId: form.deploymentTargetId"); diff --git a/platform_web/pages/ServerDetailPage.test.tsx b/platform_web/pages/ServerDetailPage.test.tsx index b6aad4c..0fa5ba6 100644 --- a/platform_web/pages/ServerDetailPage.test.tsx +++ b/platform_web/pages/ServerDetailPage.test.tsx @@ -120,7 +120,7 @@ describe("ServerDetailPage config write approval", () => { expect(serverDetailPageSource).not.toContain('capability: "process.stop"'); }); - it("leaves guided deployment to the dedicated Run registration workflow", () => { + it("leaves guided deployment to the generated Run heartbeat workflow", () => { expect(serverDetailPageSource).not.toContain("canDeployServer(instance.data.state)"); expect(serverDetailPageSource).not.toContain("requestDeployment(instance.data)"); expect(serverDetailPageSource).not.toContain("platformApiClient.deployServerInstance(current.id"); diff --git a/platform_web/pages/ServerDetailPage.tsx b/platform_web/pages/ServerDetailPage.tsx index 19c2b79..254a1a0 100644 --- a/platform_web/pages/ServerDetailPage.tsx +++ b/platform_web/pages/ServerDetailPage.tsx @@ -206,7 +206,7 @@ export function ServerDetailPage(props: PageComponentProps) {

{instance.data.name}

- {instance.data.id} · 插件 {instance.data.pluginId}@{instance.data.pluginVersion} · 节点 {instance.data.runEndpointId} + {instance.data.id} · 插件 {instance.data.pluginId}@{instance.data.pluginVersion} · Run 心跳 {runEndpoint ? "已自动附着" : "等待上报"}
diff --git a/platform_web/pages/ServersPage.tsx b/platform_web/pages/ServersPage.tsx index b1f15ba..017f137 100644 --- a/platform_web/pages/ServersPage.tsx +++ b/platform_web/pages/ServersPage.tsx @@ -22,7 +22,6 @@ import type { PageComponentProps } from "../contracts/page"; import { canDeleteServer, defaultServerCreateForm, - endpointLabel, pluginCreateInputDefaults, runtimeObservationFreshness, type ServerCreateFormState @@ -72,7 +71,7 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr const [metricsError, setMetricsError] = useState(""); const [keyword, setKeyword] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); - const [form, setForm] = useState(() => defaultServerCreateForm([], [])); + const [form, setForm] = useState(() => defaultServerCreateForm([])); const [showCreate, setShowCreate] = useState(false); const [editDeployment, setEditDeployment] = useState<{ instance: ServerInstanceResponse; deployment: import("../api/types").ServerDeploymentResponse } | null>(null); const runtimeTask = useRuntimeTaskController(); @@ -98,15 +97,9 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr setJobs(jobResponse.items); if (showLoading) setForm((current) => { const plugin = pluginResponse.items.find((item) => item.id === current.pluginId) ?? pluginResponse.items[0]; - const profileKey = plugin?.runtimeProfiles?.lifecycleProfiles?.some((profile) => profile.key === current.profileKey) - ? current.profileKey - : plugin?.runtimeProfiles?.lifecycleProfiles?.[0]?.key ?? ""; return { ...current, pluginId: plugin?.id ?? "", - profileKey, - bindings: plugin?.id === current.pluginId && profileKey === current.profileKey ? current.bindings : {}, - runEndpointId: endpointResponse.items.some((endpoint) => endpoint.id === current.runEndpointId) ? current.runEndpointId : "" }; }); if (showLoading) { @@ -170,8 +163,8 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr const operationId = operations.begin({ intent: "创建服务器", targetKind: "server", targetId: "platform", requester: session.displayName }); try { const result = await platformApiClient.createServerWorkflow(serverCreateRequestFromForm(nextForm)); - operations.succeed(operationId, result.job.id ? `已创建实例 ${result.instance.id},安装任务 ${result.job.id} 已派发` : `已创建服务器 ${result.instance.id};部署方式和启动项已随创建向导保存,请生成并启动专属 Run。`, result.job.id ? result.job : undefined); - setForm(defaultServerCreateForm(plugins, endpoints)); + operations.succeed(operationId, result.job.id ? `已创建实例 ${result.instance.id},安装任务 ${result.job.id} 已派发` : `已创建服务器 ${result.instance.id};部署方式和启动项已随创建向导保存,请生成并启动 Run。`, result.job.id ? result.job : undefined); + setForm(defaultServerCreateForm(plugins)); setShowCreate(false); await refresh(); onNavigate("serverDetail", { serverId: result.instance.id }); @@ -184,7 +177,7 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr try { const deployment = await platformApiClient.getServerDeployment(instance.id); const plugin = plugins.find((item) => item.id === instance.pluginId); - setForm({ ...defaultServerCreateForm(plugins, endpoints), name: instance.name, pluginId: instance.pluginId, runEndpointId: instance.runEndpointId, profileKey: deployment.profileKey ?? plugin?.runtimeProfiles?.lifecycleProfiles?.[0]?.key ?? "", createInputs: deployment.createInputs ?? pluginCreateInputDefaults(plugin), deploymentMode: deployment.mode ?? "guided-install", shell: deployment.shell ?? "" }); + setForm({ ...defaultServerCreateForm(plugins), name: instance.name, pluginId: instance.pluginId, createInputs: deployment.createInputs ?? pluginCreateInputDefaults(plugin), deploymentMode: deployment.mode ?? "guided-install", shell: deployment.shell ?? "" }); setEditDeployment({ instance, deployment }); } catch (error) { const operationId = operations.begin({ intent: "读取部署设置", targetKind: "server", targetId: instance.id, requester: session.displayName }); @@ -197,7 +190,7 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr const { instance } = editDeployment; const operationId = operations.begin({ intent: "更新部署定义", targetKind: "server", targetId: instance.id, requester: session.displayName }); try { - await platformApiClient.updateServerDeployment(instance.id, { runEndpointId: nextForm.runEndpointId || undefined, mode: nextForm.deploymentMode, profileKey: nextForm.profileKey || undefined, createInputs: nextForm.createInputs, serverRoot: nextForm.serverRoot.trim() || undefined, workingDirectory: nextForm.workingDirectory.trim() || undefined, installCommand: nextForm.installCommand.trim() || undefined, startCommand: nextForm.startCommand.trim() || undefined, stopCommand: nextForm.stopCommand.trim() || undefined, statusCommand: nextForm.statusCommand.trim() || undefined, shell: nextForm.shell || undefined }); + await platformApiClient.updateServerDeployment(instance.id, { mode: nextForm.deploymentMode, createInputs: nextForm.createInputs, serverRoot: nextForm.serverRoot.trim() || undefined, workingDirectory: nextForm.workingDirectory.trim() || undefined, installCommand: nextForm.installCommand.trim() || undefined, startCommand: nextForm.startCommand.trim() || undefined, stopCommand: nextForm.stopCommand.trim() || undefined, statusCommand: nextForm.statusCommand.trim() || undefined, shell: nextForm.shell || undefined }); operations.succeed(operationId, "部署设置已保存;路径和命令保持受保护状态。"); setEditDeployment(null); await refresh(); @@ -504,8 +497,8 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr
)} - setShowCreate(false)} onSubmit={handleCreate} /> - platformApiClient.revealServerDeployment(editDeployment?.instance.id ?? "")} onClose={() => setEditDeployment(null)} onSubmit={handleUpdateDeployment} /> + setShowCreate(false)} onSubmit={handleCreate} /> + platformApiClient.revealServerDeployment(editDeployment?.instance.id ?? "")} onClose={() => setEditDeployment(null)} onSubmit={handleUpdateDeployment} /> { - it("derives logical binding fields from the selected profile", () => { - expect(runtimeBindingFields(plugin, "local")).toEqual([ - { key: "java-runtime", required: false, sensitive: false }, - { key: "log-source", required: true, sensitive: false }, - { key: "package-source", required: false, sensitive: false }, - { key: "rcon.password", required: true, sensitive: true }, - { key: "server-root", required: true, sensitive: false } - ]); - expect(runtimeBindingFields(plugin, "local").some((field) => field.key === "ftp.profile")).toBe(false); - }); - it("submits deployment inputs without binding a Run or runtime profile", () => { - const form = defaultServerCreateForm([plugin], []); - expect(form.profileKey).toBe("local"); + const form = defaultServerCreateForm([plugin]); expect( serverCreateRequestFromForm( { ...form, id: " server-1 ", name: " Runtime Server ", - bindings: { "server-root": " runtime.server-root ", "rcon.password": " secret://runtime/server-1/rcon ", "java-runtime": " " } }, 17 ) @@ -79,13 +66,10 @@ describe("runtime profile server creation contracts", () => { }); it("maps the create UI to a minimal server request", () => { - const form = defaultServerCreateForm([plugin], []); + const form = defaultServerCreateForm([plugin]); const request = minimalServerCreateRequestFromForm({ ...form, name: " Minimal Runtime Server ", - deploymentTargetId: "run-builder", - runEndpointId: "run-existing", - bindings: { "rcon.password": "secret://must-not-submit" }, serverRoot: "/srv/must-not-submit" }, 16); @@ -98,8 +82,8 @@ describe("runtime profile server creation contracts", () => { }); it("generates server instance ids from the visible server name", () => { - const form = defaultServerCreateForm([plugin], []); - const request = serverCreateRequestFromForm({ ...form, name: " Runtime Server ", bindings: {} }, 17); + const form = defaultServerCreateForm([plugin]); + const request = serverCreateRequestFromForm({ ...form, name: " Runtime Server " }, 17); expect(request).toMatchObject({ id: "server-runtime-server-17", @@ -119,7 +103,7 @@ describe("runtime profile server creation contracts", () => { }); it("keeps complete paths and commands in a write-only deployment payload", () => { - const form = defaultServerCreateForm([plugin], []); + const form = defaultServerCreateForm([plugin]); const request = serverCreateRequestFromForm({ ...form, name: "Venv Server", deploymentMode: "custom-command", serverRoot: "/srv/venv-server", workingDirectory: "/srv/venv-server", startCommand: "/srv/venv-server/.venv/bin/python server.py", shell: "" }, 19); expect("runEndpointId" in request).toBe(false); expect("profileKey" in request).toBe(false); diff --git a/scripts/local-debug/smoke.sh b/scripts/local-debug/smoke.sh index a5a857c..0f94b9d 100755 --- a/scripts/local-debug/smoke.sh +++ b/scripts/local-debug/smoke.sh @@ -647,15 +647,15 @@ launch_generated_run() { fi } -wait_for_generated_run_registration_and_heartbeat() { +wait_for_generated_run_heartbeat() { local registration_file="$WORK_DIR/generated-run-registration.response.json" local heartbeat_file="$WORK_DIR/generated-run-heartbeat.response.json" local first_heartbeat="" rm -f "$registration_file" "$heartbeat_file" - printf 'waiting for generated Run endpoint %s registration\n' "$GENERATED_RUN_ENDPOINT_ID" + printf 'waiting for generated Run heartbeat %s\n' "$GENERATED_RUN_ENDPOINT_ID" for _ in $(seq 1 45); do if [[ -f "$GENERATED_RUN_PID_FILE" ]] && ! kill -0 "$(<"$GENERATED_RUN_PID_FILE")" 2>/dev/null; then - printf 'generated Run exited before registration; see %s\n' "$GENERATED_RUN_LOG" >&2 + printf 'generated Run exited before heartbeat; see %s\n' "$GENERATED_RUN_LOG" >&2 return 1 fi if json_get "$API_URL/run/endpoints/$GENERATED_RUN_ENDPOINT_ID" "$registration_file" "${AUTH_HEADER[@]}" 2>/dev/null; then @@ -677,13 +677,13 @@ NODE sleep 1 done if [[ -z "$first_heartbeat" ]]; then - printf 'generated Run endpoint %s did not register with a safe capability report\n' "$GENERATED_RUN_ENDPOINT_ID" >&2 + printf 'generated Run endpoint %s did not report a safe heartbeat\n' "$GENERATED_RUN_ENDPOINT_ID" >&2 [[ -f "$GENERATED_RUN_LOG" ]] && sed -n '1,160p' "$GENERATED_RUN_LOG" >&2 return 1 fi reject_forbidden_fragments "$registration_file" - printf 'waiting for generated Run endpoint %s heartbeat\n' "$GENERATED_RUN_ENDPOINT_ID" + printf 'waiting for a subsequent generated Run heartbeat %s\n' "$GENERATED_RUN_ENDPOINT_ID" for _ in $(seq 1 45); do if json_get "$API_URL/run/endpoints/$GENERATED_RUN_ENDPOINT_ID" "$heartbeat_file" "${AUTH_HEADER[@]}" 2>/dev/null && node - "$heartbeat_file" "$GENERATED_RUN_ENDPOINT_ID" "$first_heartbeat" <<'NODE' const fs = require("fs"); @@ -700,7 +700,7 @@ NODE fi sleep 1 done - printf 'generated Run endpoint %s did not report a subsequent heartbeat\n' "$GENERATED_RUN_ENDPOINT_ID" >&2 + printf 'generated Run %s did not report a subsequent heartbeat\n' "$GENERATED_RUN_ENDPOINT_ID" >&2 [[ -f "$GENERATED_RUN_LOG" ]] && sed -n '1,160p' "$GENERATED_RUN_LOG" >&2 return 1 } @@ -1159,9 +1159,7 @@ JSON cat >"$WORK_DIR/log-session-deployment.request.json" <"$WORK_DIR/server-deployment.request.json" <"$WORK_DIR/server-deployment.response.json" diff --git a/scripts/local-debug/start.sh b/scripts/local-debug/start.sh index fa50eeb..9b874b0 100755 --- a/scripts/local-debug/start.sh +++ b/scripts/local-debug/start.sh @@ -188,54 +188,6 @@ wait_for_url() { return 1 } -wait_for_run_registration() { - local attempts="${1:-45}" - local run_pid_file="$LOCAL_DEBUG_PID_DIR/run.pid" - printf 'waiting for run endpoint %s registration\n' "$RUN_ENDPOINT_ID" - for _ in $(seq 1 "$attempts"); do - if ! managed_pid_running "$run_pid_file"; then - printf 'run exited before endpoint registration; see %s\n' "$LOCAL_DEBUG_LOG_DIR/run.log" >&2 - return 1 - fi - if run_endpoint_has_recent_heartbeat; then - printf 'run endpoint %s is registered\n' "$RUN_ENDPOINT_ID" - return 0 - fi - sleep 1 - done - printf 'run endpoint %s did not register before timeout\n' "$RUN_ENDPOINT_ID" >&2 - return 1 -} - -run_endpoint_has_recent_heartbeat() { - [[ -f "$PLATFORM_METADATA_PATH" ]] || return 1 - node -e ' -const fs = require("fs"); -const metadata = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); -const endpointID = process.argv[2]; -function findEndpoint(value) { - if (!value || typeof value !== "object") return null; - if ((value.ID || value.id) === endpointID && (value.LastHeartbeatAt || value.lastHeartbeatAt)) return value; - if (Array.isArray(value)) { - for (const item of value) { - const found = findEndpoint(item); - if (found) return found; - } - return null; - } - for (const item of Object.values(value)) { - const found = findEndpoint(item); - if (found) return found; - } - return null; -} -const endpoint = findEndpoint(metadata); -const heartbeat = endpoint && (endpoint.LastHeartbeatAt || endpoint.lastHeartbeatAt); -const ageMilliseconds = heartbeat ? Date.now() - Date.parse(heartbeat) : Number.POSITIVE_INFINITY; -process.exit(Number.isFinite(ageMilliseconds) && ageMilliseconds >= 0 && ageMilliseconds < 30000 ? 0 : 1); -' "$PLATFORM_METADATA_PATH" "$RUN_ENDPOINT_ID" -} - printf 'local debug root: %s\n' "$LOCAL_DEBUG_ROOT" printf 'platform: %s\n' "$(local_debug_platform_url)" printf 'platform_web: %s\n' "$(local_debug_web_url)" @@ -303,8 +255,6 @@ start_service run "$(dirname "$RUN_BOOTSTRAP_BIN")" env \ RUN_RETRY_BACKOFF_MS="$RUN_RETRY_BACKOFF_MS" \ "$RUN_BOOTSTRAP_BIN" -wait_for_run_registration - cat <