184 lines
24 KiB
Markdown
184 lines
24 KiB
Markdown
## Context
|
|
|
|
The current SCUM implementation has two disconnected local player models and an unimplemented real-data path. Login ingestion can upsert `GamePlayer` only after Run has already supplied synthetic `eventType/playerId/playerName` fields, while the repository's only SCUM parser recognizes an invented `SCUM LOGIN/LOGOUT` console format rather than the game's real login file. The plugin manifest declares six SQLite query keys but contains no query asset, workflow dispatch changes local statuses without creating read jobs, and job completion never calls the SCUM row-ingestion service. The page therefore renders empty local objects, fabricated maintenance/backup references, hard-coded actions, a non-georeferenced dot board, and a hard-coded gift.
|
|
|
|
The reference `scum_robot` demonstrates the useful direction—tail the current server's login log, create a server-scoped local player, and let a machine-side process query its current SCUM.db—but its static SCUM structs are not migrated or authoritative, its production roster/world refresh mostly parses administrator-command output, and its raw SQL, permissions, XML rewrite, early gift-success recording, and forced player death are unsafe to copy.
|
|
|
|
This change crosses `platform/`, `platform_web/`, the SCUM plugin and SDK, and the independent Run repository. It must preserve the repository boundary: the SCUM plugin owns game-specific formats and declarations; Platform owns authorization, durable local business data, jobs, and safe APIs; Run owns generic machine-side execution and observed runtime facts. Browser code never receives SQL, raw XML, database paths, credentials, sockets, or raw RCON.
|
|
|
|
The implementation is blocked from declaring production SQL or `855` semantics until a read-only probe against the current bound service produces evidence. An unbound copied/reference database, historical cache, or generated model is only a hypothesis source. Run may create an execution-time read-only snapshot of the active database only when the snapshot is fenced to the current binding/database identity, timestamped, checksummed, short-lived, and invalidated on rebinding or schema change.
|
|
|
|
## Goals / Non-Goals
|
|
|
|
**Goals:**
|
|
|
|
- Replace SCUM projection/observation/Workflow product concepts with automatic synchronization into dedicated local records.
|
|
- Create player identity and session data from authentic SCUM login/logout logs and enrich it only with verified current-service database facts.
|
|
- Make the plugin own versioned parsers, SQL, result contracts, map metadata, RCON templates, and controlled mutation metadata while Run remains generic.
|
|
- Provide trustworthy user, squad, map, gift, and AI surfaces with explicit effective-user permissions and no fabricated facts or evidence.
|
|
- Keep unknown values null/absent, preserve the last successfully synchronized rows after a failed sync, and show ordinary connection/last-sync information without projection/freshness jargon or manual refresh actions.
|
|
- Make manual and AI-originated writes use one typed authorization, review, dispatch, and confirmation path.
|
|
- Preserve required server deployment, metadata, and administrator controls after removing the `管理` tab.
|
|
|
|
**Non-Goals:**
|
|
|
|
- Do not add a `run/` tree, billing, cloud-host sales, agent-provider workflows, or unrelated marketplace behavior.
|
|
- Do not use a repository/reference/cached `SCUM.db`, trust `scum_robot/model/scum_server.go` as a production schema, or ship guessed joins, enum values, coordinates, ownership, balances, or default zero values.
|
|
- Do not expose arbitrary SQL, raw RCON, host paths, direct sockets, SCUM.db credentials, or raw `template_xml` to Platform Web or AI.
|
|
- Do not hardcode SCUM tables, query keys, commands, or lifecycle behavior in Platform or Run.
|
|
- Do not provide a Workflow status page, refresh/audit buttons, a manual synchronization API, or product-facing projection/observation terminology.
|
|
- Do not automatically kill/respawn a character, invent a missing XML skill, or claim a write/gift succeeded before conclusive execution and confirmation evidence exists.
|
|
- Do not promise a sub-second realtime map when the verified database or companion source cannot supply that cadence.
|
|
|
|
## Decisions
|
|
|
|
### 1. Bootstrap a generic probe, then treat current-service discovery as a release gate
|
|
|
|
The first cross-repository prerequisite is to confirm or add the minimal generic, bounded diagnostic probe executor in the independent Run repository and deploy that compatible Run to the current binding. Platform and Run must establish the probe request/result envelope, binding fence, safe limits, and redaction without introducing any SCUM-specific table or path knowledge.
|
|
|
|
Only then will the production implementation execute bounded, read-only probes through the currently bound Run. For operator-directed discovery, a server-local diagnostic script may read the active database in place on the game server host when it is bounded, query-only, redacted, and recorded as diagnostic evidence rather than a Platform/plugin/browser data path. In both cases, the evidence must include `sqlite_master`, `PRAGMA table_info`, foreign-key/index information for candidate tables, small redacted samples, relevant enum distributions, coordinate ranges/cadence, and read-lock behavior. It must confirm player/profile/entity joins, squad rank meaning, flag ownership relationships, vehicle identity fields, currency types, and whether/where a mutable profile XML payload actually exists.
|
|
|
|
Current-service schema, log structure, and content-feature baselines are an explicit prerequisite for further adapter or write declarations. The change stores a redacted all-table schema inventory, process-adjacent log pattern inventory, XML tag/attribute/value-shape summaries, and log skeleton marker sets under `evidence/`; capability work must consume those local baselines rather than repeatedly probing piecemeal or asking the operator for raw XML/log/database files.
|
|
|
|
No production query or mutation asset may be merged until the probe evidence is captured in the change or an explicitly referenced test artifact. The probe is diagnostic-only, is not exposed as a browser action, and performs no write.
|
|
|
|
Alternative considered: start with the reference structs and fix queries after deployment. Rejected because the reference models are incomplete hypotheses and the current implementation already failed by treating guessed fields as facts.
|
|
|
|
### 2. Package game-specific behavior as signed plugin assets
|
|
|
|
The SCUM plugin will contain versioned assets for:
|
|
|
|
- login/logout parsing and field mapping;
|
|
- read-only SQLite queries for player details, squads, squad members, vehicles, flags, and positions;
|
|
- result JSON schemas and schema-version compatibility;
|
|
- map identity, asset reference, world bounds, and coordinate transform;
|
|
- RCON command templates for supported economy and gift operations;
|
|
- database/XML mutation declarations for fields without a safe game command.
|
|
|
|
Manifest declarations will reference each asset and its digest. The generated Run package will contain those immutable assets. Platform queues a template key plus bounded parameters and expected digest; it does not construct SCUM SQL or command text. Run verifies the packaged asset/digest and executes it through a generic capability.
|
|
|
|
Alternative considered: make Run implement the six `scum.*` keys. Rejected because it makes Run game-aware and requires a Run release for every SCUM schema change. Alternative considered: accept SQL from the browser or a general Platform endpoint. Rejected because it creates an arbitrary database interface.
|
|
|
|
### 3. Add generic bounded Run contracts instead of SCUM workflows
|
|
|
|
The external Run contract will support:
|
|
|
|
- a generic current-target schema probe;
|
|
- read-only SQLite template execution with parameter binding, a single SELECT/CTE or approved introspection statement, query-only mode, timeout, row/result-size limits, and no `ATTACH`, extension loading, or mutation;
|
|
- plugin-owned typed RCON template execution;
|
|
- plugin-owned guarded SQLite/XML mutation execution in a bounded transaction;
|
|
- typed result envelopes containing server/plugin binding, template key, asset digest, schema version, observed time, checksum, rows or affected-row count, and a safe error/result code.
|
|
|
|
Platform will use ordinary durable Run jobs and the existing channel priority/lease/fencing rules. A new terminal-job hook validates the result envelope and schema, then invokes the matching local sync or write-confirmation service. There is no SCUM Workflow instance/step scheduler.
|
|
|
|
Alternative considered: repair the current SCUM workflow engine. Rejected because synchronization is a background data-ingestion concern, direct resource mutations already have a single target operation, and the workflow layer currently adds user-visible states without performing work.
|
|
|
|
### 4. Persist SCUM facts in dedicated normalized local stores
|
|
|
|
The local model will use dedicated repositories for at least:
|
|
|
|
- `scum_players` with unique `(server_instance_id, external_player_id)`;
|
|
- `scum_player_sessions` with durable source-event identity;
|
|
- `scum_player_details` for nullable profile/economy/squad facts and source checksum/time;
|
|
- `scum_squads` and `scum_squad_members`;
|
|
- `scum_vehicles`, `scum_flags`, and `scum_positions`;
|
|
- `scum_gift_packages`, `scum_gift_items`, and `scum_gift_deliveries`;
|
|
- internal `scum_sync_cursors` and successful sync generations.
|
|
|
|
High-frequency SCUM data will not be appended to the global metadata snapshot. The `mysql` backend will use normalized MySQL tables and migrations. The default `file` backend will use a platform-owned SQLite sidecar under the configured data directory; the `memory` backend will use an in-memory implementation for tests. The storage interface remains under `platform/repo`, database models/migrations under their fixed platform directories, and domain/API types remain separate.
|
|
|
|
A full successful resource scan uses a generation marker: rows are upserted transactionally, and records missing from the completed generation can be marked absent. A partial or failed scan never deletes or overwrites the last successful rows. Missing source fields remain `NULL`; zero is stored only when the source explicitly reports zero.
|
|
|
|
Alternative considered: retain the existing generic snapshot repositories and rename projection types. Rejected because every coordinate update would rewrite the whole metadata snapshot and would preserve the misleading data architecture the change is intended to remove.
|
|
|
|
### 5. Synchronize automatically from logs, binding state, and declared cadence
|
|
|
|
Run tails the plugin-declared login source and applies the plugin parser locally. Each typed login/logout event carries both a transport cursor `(source identity, stream generation, sequence)` for acknowledgement/resume and a privacy-safe logical event identity that is native to the log when available or derived from normalized non-network event fields and remains stable across rotation-overlap replay. A successful login atomically upserts the player, updates the current display name and last-seen time, and opens a session; logout closes the matching session without regressing a newer session. The source contract handles file rotation, truncation, Run restart, partial lines, and duplicate/overlapping batches without duplicating a logical event. Malformed lines, failed-login attempts, and events from an obsolete binding never create a player. Raw IP/network material is discarded before durable storage or logical fingerprinting.
|
|
|
|
The sync scheduler starts an initial bounded database scan when a compatible Run binding becomes ready, schedules resource scans at plugin-declared intervals with jitter and per-server concurrency limits, and may enqueue a bounded player-detail lookup after a new login. Positions use the measured safe cadence. If SCUM.db cannot provide the required map cadence, the plugin must declare a companion position-event source; the product must not compensate with fabricated motion or aggressive unsafe polling.
|
|
|
|
Platform Web always reads local resource APIs and receives updates through a platform-owned event stream/SSE. Opening a page never dispatches a database query, and there is no user refresh button.
|
|
|
|
Alternative considered: query Run on every page load. Rejected because it couples browser latency to machine connectivity, multiplies database reads, and makes pagination/filtering dependent on a remote SQLite file.
|
|
|
|
### 6. Replace the player-intelligence model with a lean roster
|
|
|
|
The SCUM product flow retains only the server-scoped player identity, current display name, first/last seen times, online session, login history, and verified player details. It removes alias-history, access-attempt, shared-IP, automatic-risk, and player-intelligence features from SCUM manifests, APIs, UI, and persistence when they have no non-SCUM consumer.
|
|
|
|
List APIs operate on the local store and support bounded pagination, search by name/external ID, online state, squad filter, and allowlisted sorting. Player detail returns nullable verified facts and last synchronized time. The UI uses a table and detail/edit surface rather than a fixed list of cards and hard-coded increments.
|
|
|
|
Alternative considered: keep the intelligence records hidden. Rejected because the user requested a smaller player model and the hidden ingestion side effects would still complicate login creation and retention.
|
|
|
|
### 7. Use one controlled-write path for manual and AI requests
|
|
|
|
Existing permission families remain explicit:
|
|
|
|
- `server.game-client.read` for local SCUM reads;
|
|
- `server.game-client.command` for declared RCON/economy/gift actions;
|
|
- `server.game-client.maintenance` plus an explicit synchronous danger confirmation for database/XML mutations.
|
|
|
|
The plugin page host must receive the current session's effective server permissions, not merely the permissions declared by the plugin/page. The backend reauthorizes every request and remains authoritative. An AI draft records the initiating user and cannot be dispatched unless that same confirming user still has the permission required for a manual request. AI never runs under an independent component write principal.
|
|
|
|
Fame and currencies use plugin-declared game commands when verified. Character attributes use a version-scoped XML patch declaration only after the active adapter confirms the actual profile XML source: Run reads the current payload, checks the expected row/checksum, changes only allowlisted named attributes or existing skill nodes, preserves all unknown nodes/attributes, updates at most one row with bound parameters, and reads back for confirmation. A real backup reference, verified offline/maintenance condition when required, before values, reason, idempotency key, and explicit confirmation are mandatory. A timestamp-shaped fake backup or maintenance string is invalid. There is no separate platform-admin approval domain, audit-initiation action, or approval queue.
|
|
|
|
`855` is not a field key. It can be declared only as a preset that expands to explicitly named attributes after its exact mapping is confirmed. Any respawn/death action needed to activate values is a separate explicit destructive operation and is never part of the default attribute save.
|
|
|
|
Alternative considered: reuse `player.attribute.855.set` and a generic integer field mutation. Rejected because the source is XML containing several named values. Alternative considered: let AI or Platform generate raw SQL/XML. Rejected because it leaks protected content and bypasses plugin version fencing.
|
|
|
|
### 8. Model gifts as local business data with typed delivery
|
|
|
|
Gift packages and items are server/plugin-instance scoped and have normal create, read, update, and delete operations, bounded eligibility/quantity rules, and explicit active state. A delivery freezes the target player and item definitions, atomically reserves any period-limited entitlement, uses a server-scoped idempotency key, and dispatches only a plugin-declared typed command. In-progress and unknown outcomes retain their reservation so concurrent requests cannot exceed the limit; only a conclusive non-executed failure may release it. The API reports creation/dispatch separately from delivered success. `delivered` is stored only after conclusive Run success; failed and unknown outcomes retain safe evidence and never auto-redeliver.
|
|
|
|
Alternative considered: preserve the current hard-coded starter package and notification. Rejected because neither is backed by catalog data. Alternative considered: copy `scum_robot`'s Redis command list. Rejected because it stores success before execution and accepts raw game command strings.
|
|
|
|
### 9. Make the SCUM surface contain exactly five tabs
|
|
|
|
The server detail order is `用户管理`, `队伍管理`, `实时地图`, `礼包管理`, `AI 助手`; the first plugin page is the default. `Workflow 状态` is removed from the manifest and the generic `管理` entry is excluded for the SCUM detail view. Deployment remains reachable from server-list actions. Server name and administrator membership move into a server-settings drawer opened from the detail header.
|
|
|
|
All copy and actions referring to projection, observation, freshness/staleness, refresh projection, refresh real data, initiate audit, pending review counts, or Workflow state are removed. Pages may show ordinary connection status and last synchronized/collected time. Empty states describe the missing business record without offering a manual refresh or implying generated data.
|
|
|
|
AI configuration keeps the existing reviewable config-diff path. Player-operation AI suggestions produce the same named-field edit draft used by the manual form.
|
|
|
|
Alternative considered: hide the two tabs without moving their content or changing the default route. Rejected because it would strand required server controls and leave invalid route fallbacks.
|
|
|
|
### 10. Keep legacy completed changes historical instead of importing obsolete specs
|
|
|
|
The repository's main OpenSpec baseline currently contains no SCUM capabilities, while the changes that introduced player intelligence, trajectories, the versioned gift catalog, player-state patching, projection/Workflow automation, controlled operations, and the old product surface are complete but unarchived. This replacement therefore declares only uniquely named `ADDED` capabilities. It supersedes those historical artifacts through explicit implementation deletion and acceptance coverage; it does not first archive them into the baseline and then attempt to remove them.
|
|
|
|
The old completed changes must not later be archived in a way that publishes their obsolete SCUM requirements as current baseline specs. Historical cleanup is a separate reviewed consolidation after this replacement is implemented, using skip-specs or an equivalent approach that preserves history without resurrecting removed behavior.
|
|
|
|
## Risks / Trade-offs
|
|
|
|
- [Risk] SCUM schema changes between game versions. → Require live probe evidence, schema-versioned assets, exact result schemas, digest fencing, and a disabled/incompatible state rather than fallback queries.
|
|
- [Risk] Reading the live SQLite file can block or observe inconsistent rows. → Use query-only connections, short timeouts, bounded scans, measured cadence, jitter/backoff, and keep the last completed generation on failure.
|
|
- [Risk] Game saves may overwrite direct database/XML changes. → Verify offline/maintenance semantics against the current service, require before/checksum guards and backup evidence, and disable unverified mutations.
|
|
- [Risk] XML reserialization can destroy fields unknown to the current plugin. → Patch only named attributes/existing nodes while preserving raw document structure, then confirm by readback; never synthesize missing skills.
|
|
- [Risk] Cross-repository Run support can lag the browser release. → Gate synchronization and write UI on compatible asset-executor capabilities and deploy Run support before enabling the new pages.
|
|
- [Risk] Dedicated local tables add a second persistence path for the file backend. → Keep it behind a narrow `SCUMStore` interface, use explicit migrations and health checks, and fail the SCUM feature closed without affecting core server lifecycle.
|
|
- [Risk] Position polling can generate large write volume or still not be realtime. → Measure source cadence, retain only current positions by default, make history explicitly bounded, and use a declared companion event source when database cadence is inadequate.
|
|
- [Risk] Removing `管理` can make server settings undiscoverable. → Add header-level server settings and preserve server-list deployment actions before removing the tab.
|
|
- [Risk] Old projection/Workflow snapshot data can look real after upgrade. → Never migrate it into the new tables; populate only from post-upgrade authenticated logs/current-service sync.
|
|
|
|
## Migration Plan
|
|
|
|
1. Confirm or implement the minimal generic diagnostic probe contract in the independent Run repository, deploy a compatible Run to the active binding, and verify binding fencing/redaction without adding SCUM-specific behavior to Run.
|
|
2. Probe the active service database in read-only mode, using either the durable Run path for product acceptance or an explicitly operator-directed server-local diagnostic for discovery only; capture schema/cadence/lock evidence and confirm whether the `855` preset has a real named-attribute mapping. Stop any affected capability if the evidence cannot support safe query or mutation declarations.
|
|
3. Add plugin/SDK/validator contracts and immutable SCUM parser/query/map/command/mutation assets backed by that evidence; implement and deploy the remaining compatible generic Run executors in the independent repository.
|
|
4. Add the dedicated SCUM store implementations, migrations, health checks, local resource contracts, and automatic sync scheduler behind a disabled-by-default compatibility gate.
|
|
5. Wire authentic, rotation-safe login events and terminal Run query results into local transactional upserts; verify initial and periodic sync against the current service without enabling writes.
|
|
6. Build the player, squad, map, and gift APIs/pages, effective-permission context, server-settings relocation, and preserved AI configuration flow against the new local records.
|
|
7. Enable controlled RCON writes, then separately enable guarded XML mutations only after actual source mapping, backup/offline/readback verification, effective permission, and explicit confirmation succeed on the current service.
|
|
8. Cut navigation and clients to the five new tabs; remove Workflow/projection/observation APIs, services, repositories, snapshot fields, page actions, banned copy, fake gifts/map/actions, and obsolete tests/spec assumptions.
|
|
9. Run focused backend/frontend/plugin tests, external Run contract acceptance, browser acceptance against real synchronized data, `scripts/check-structure.sh`, and `openspec validate replace-scum-projections-with-real-data-management --strict` before marking tasks complete.
|
|
|
|
Rollback disables the SCUM compatibility gate and write actions while leaving the new local tables for diagnosis. It must not re-enable fake projection data or migrate new facts back into old Workflow/projection records. Core server lifecycle, logs, jobs, and AI provider management remain available.
|
|
|
|
## Open Questions
|
|
|
|
- What are the exact current-service tables, columns, joins, enum values, coordinate cadence, and safe read behavior?
|
|
- What exact named attribute mapping does the operator mean by the `855` preset, and what game action—if any—is required for those values to take effect?
|
|
- Does the verified current SCUM version support safe RCON readback for fame/currencies, or must confirmation use a database query?
|
|
- Which map asset/version and coordinate transform are authorized for first-party redistribution?
|
|
- Which gift item aliases and delivery transports can be verified without exposing or accepting arbitrary commands?
|
|
- Which pure-Go SQLite driver and migration mechanism will be pinned for the default file-backend sidecar after dependency and build verification?
|