24 KiB
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, trustscum_robot/model/scum_server.goas 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_xmlto 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 and log structure baselines are an explicit prerequisite for further adapter or write declarations. The change stores a redacted all-table schema inventory and process-adjacent log pattern inventory 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_playerswith unique(server_instance_id, external_player_id);scum_player_sessionswith durable source-event identity;scum_player_detailsfor nullable profile/economy/squad facts and source checksum/time;scum_squadsandscum_squad_members;scum_vehicles,scum_flags, andscum_positions;scum_gift_packages,scum_gift_items, andscum_gift_deliveries;- internal
scum_sync_cursorsand 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.readfor local SCUM reads;server.game-client.commandfor declared RCON/economy/gift actions;server.game-client.maintenanceplus 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
SCUMStoreinterface, 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
- 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.
- 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
855preset has a real named-attribute mapping. Stop any affected capability if the evidence cannot support safe query or mutation declarations. - 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.
- Add the dedicated SCUM store implementations, migrations, health checks, local resource contracts, and automatic sync scheduler behind a disabled-by-default compatibility gate.
- 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.
- 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.
- 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.
- 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.
- Run focused backend/frontend/plugin tests, external Run contract acceptance, browser acceptance against real synchronized data,
scripts/check-structure.sh, andopenspec validate replace-scum-projections-with-real-data-management --strictbefore 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
855preset, 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?