docs(openspec): propose real SCUM data management

This commit is contained in:
npc0-hue
2026-08-11 10:46:58 +08:00
parent 12fed856f7
commit 9184f83480
11 changed files with 1067 additions and 0 deletions
@@ -0,0 +1,117 @@
## ADDED Requirements
### Requirement: Effective-user permission enforcement
Every SCUM state-changing request SHALL be authorized against the current user's effective target-server permission at UI presentation, request creation, review/confirmation, and dispatch, with the backend as the authoritative enforcement point.
#### Scenario: Read-only operator opens a writable surface
- **WHEN** a user has `server.game-client.read` but lacks the required write permission
- **THEN** data remains readable, write controls are hidden or disabled with a textual reason, and a direct API attempt is forbidden
#### Scenario: Economy command is requested
- **WHEN** a user edits Fame, cash, or gold
- **THEN** Platform requires `server.game-client.command` and target-server access before accepting the named-field request
#### Scenario: Gift delivery is requested
- **WHEN** a user sends a declared gift package to a server-local player
- **THEN** Platform requires `server.game-client.command`, target-server access, and the gift capability's eligibility/idempotency checks before dispatch
#### Scenario: Database/XML attribute edit is requested
- **WHEN** a user edits a maintenance-level character attribute or preset
- **THEN** Platform requires `server.game-client.maintenance`, target-server access, a synchronous explicit danger confirmation, and the declared safety gates before dispatch; it SHALL NOT create a separate platform-admin approval domain or approval queue
#### Scenario: Plugin page receives permissions
- **WHEN** Platform Web constructs the plugin page host context
- **THEN** it supplies the current session's effective permissions rather than treating manifest-declared permissions or callback presence as proof of authority
### Requirement: Manual and AI write-path parity
Manual forms and AI/Agent-originated SCUM changes SHALL create the same named-field draft and SHALL use the same allowlist, validation, effective-user authorization, review, dispatch, and confirmation service.
#### Scenario: AI suggests an allowed player change
- **WHEN** a user with the required write permission asks AI to change a declared player field
- **THEN** AI produces a reviewable target/player/field/current/proposed diff and no execution occurs until the user explicitly confirms it
#### Scenario: AI requester lacks write permission
- **WHEN** AI can generate or display a suggestion for a user who lacks the field's write permission
- **THEN** the apply action remains unavailable and an API attempt is forbidden; AI receives no component-principal bypass
#### Scenario: AI invents a field or protected payload
- **WHEN** an AI response contains an undeclared field, raw SQL, raw XML, raw RCON, path, credential, or arbitrary command
- **THEN** Platform rejects the draft and stores no executable protected payload from it
### Requirement: Plugin-owned immutable write assets
The SCUM plugin SHALL own versioned typed command templates and guarded mutation declarations, and every executable write asset SHALL be digest-referenced by the plugin manifest and generated Run package.
#### Scenario: Declared write is dispatched
- **WHEN** Platform dispatches a confirmed named-field command or mutation draft
- **THEN** Run executes only the packaged asset whose plugin ID/version, adapter/schema version, action key, server binding, and digest all match the reviewed draft
#### Scenario: Write asset identity changed
- **WHEN** an action key, adapter version, packaged asset, or digest no longer matches the reviewed draft
- **THEN** Platform or Run rejects the write before any game command or database mutation executes
### Requirement: Plugin-owned command execution
SCUM writes with a verified game command SHALL use plugin-owned typed command templates, and Platform and Run SHALL NOT hardcode SCUM command strings.
#### Scenario: Fame or currency is changed
- **WHEN** the compatible plugin adapter declares a supported Fame, normal-currency, or gold command
- **THEN** Platform dispatches its template key and validated parameters through the generic Run/RCON transport and performs the declared confirmation read
#### Scenario: Command template is not compatible
- **WHEN** the current plugin/game adapter cannot verify the command and confirmation contract
- **THEN** the write capability is disabled and SHALL NOT fall back to direct SQLite mutation or Platform-built command text
### Requirement: Named and preserving XML attribute mutation
Character attribute writes SHALL target version-scoped named attributes or existing skill nodes in the profile XML source verified by the active current-service adapter and SHALL preserve all untargeted XML content.
#### Scenario: One allowed attribute changes
- **WHEN** Run reads a valid current XML document and applies an authorized and explicitly confirmed named-field change
- **THEN** only the targeted allowlisted attribute/node changes and unknown attributes, nodes, ordering-sensitive extensions, and all other values remain semantically intact
#### Scenario: Unknown field or missing skill is requested
- **WHEN** a requested attribute/skill is absent from the version adapter or the XML lacks the targeted existing skill node
- **THEN** Run rejects the mutation and SHALL NOT invent a node, default an `Attribute` value, or rewrite the document from an incomplete struct
#### Scenario: XML is malformed
- **WHEN** the current adapter-confirmed profile XML payload cannot be parsed by the preserving patcher
- **THEN** no database write occurs and Platform receives a bounded safe failure
### Requirement: `855` is an explicit preset, not a database field
The system SHALL NOT declare or execute `855` as a table column or generic integer field; it MAY expose `855` only after an operator-confirmed preset maps it to explicit version-scoped named character attributes and values.
#### Scenario: Preset meaning is not confirmed
- **WHEN** the plugin adapter has no reviewed mapping for the `855` label
- **THEN** the preset is absent/disabled and no `fieldKey=855`, `prisoner.value`, or `0..100000` mutation can be created
#### Scenario: Confirmed preset is reviewed
- **WHEN** a compatible adapter declares the preset and an authorized user selects it
- **THEN** the review shows every named attribute's current and proposed value rather than a single opaque `855` value
### Requirement: Guarded single-row database mutation
Every database/XML mutation SHALL require a current compatible probe, target identity, expected before values/checksum, reason, idempotency key, genuine same-server backup evidence, required offline/maintenance evidence, a single-row bound, and read-after-write confirmation.
#### Scenario: Safety evidence is missing or fabricated
- **WHEN** the player's online state is unknown/unsafe, maintenance is unverified, the backup reference is absent/not restorable/not for the same database instance, or a timestamp-shaped placeholder is supplied
- **THEN** Platform rejects dispatch and no mutation job is created
#### Scenario: Current value changed
- **WHEN** the row identity, current value, XML digest, or schema fingerprint no longer matches the reviewed before state
- **THEN** Run aborts the transaction as a conflict without applying the requested value
#### Scenario: Affected row count is not one
- **WHEN** a mutation would affect zero or more than one row
- **THEN** Run rolls back and reports failure
#### Scenario: Readback does not confirm the target values
- **WHEN** execution may have occurred but the confirmation read is missing or mismatched
- **THEN** Platform SHALL NOT report success and SHALL require confirmation before any explicit retry
#### Scenario: Attribute save completes
- **WHEN** the single-row mutation and readback confirm every named target value and new digest
- **THEN** Platform records the safe result and updates local verified details without exposing the raw XML
### Requirement: No implicit destructive activation
Saving character attributes SHALL NOT implicitly kill, respawn, kick, or otherwise disrupt the player.
#### Scenario: Game requires respawn for new values
- **WHEN** current-service verification shows a respawn/death action is necessary for activation
- **THEN** that action is a separate explicitly named destructive command with its own permission and fresh user confirmation and is not automatically chained to attribute save
@@ -0,0 +1,95 @@
## ADDED Requirements
### Requirement: Server-scoped gift package CRUD
The system SHALL provide effective-user-authorized create, read, update, enable/disable, and delete operations for gift packages and typed items scoped to one server instance and compatible plugin/game version.
#### Scenario: User has gift read access only
- **WHEN** the current session has `server.game-client.read` but lacks `server.game-client.maintenance`
- **THEN** package data and delivery history remain readable while create/edit/enable/delete controls are unavailable and direct mutation requests are forbidden
#### Scenario: Operator creates a valid package
- **WHEN** an operator with effective `server.game-client.maintenance` submits a name, classification, eligibility/limit rules, and bounded quantities of plugin-catalogued item keys
- **THEN** Platform stores the package and items for the target server without storing raw RCON, shell, SQL, or arbitrary command strings
#### Scenario: Package item is invalid
- **WHEN** a package contains an unknown or incompatible item key, duplicate item, out-of-range quantity, or game-version mismatch
- **THEN** Platform rejects the change and preserves the previous package
#### Scenario: Package is deleted
- **WHEN** an authorized operator confirms deletion of a package not protected by an active delivery
- **THEN** Platform removes or retires the package without deleting immutable completed delivery facts
### Requirement: Plugin-owned immutable gift catalog and transport
Gift item aliases, compatibility rules, quantities, and delivery transport declarations SHALL come only from versioned digest-referenced assets in the plugin package and, where executable, the generated Run package.
#### Scenario: Catalog or transport digest changed
- **WHEN** a package or frozen delivery references a catalog/transport version or digest that no longer matches the active compatible plugin package
- **THEN** Platform rejects creation or dispatch and SHALL NOT translate the item into an arbitrary command or newer unreviewed alias
### Requirement: Validated gift eligibility and limits
The system SHALL evaluate package eligibility and atomically reserve database-backed per-player, per-server, and configured period limits in the server's declared timezone before a delivery can dispatch.
#### Scenario: Player belongs to another server
- **WHEN** a caller attempts to deliver a package to a player record outside the package's server instance
- **THEN** Platform rejects the request before creating a delivery or Run job
#### Scenario: Period limit is reached
- **WHEN** a player already has the allowed number of reserved, in-progress, partial, unknown, or delivered grants in the active daily/weekly or declared period boundary
- **THEN** Platform rejects another delivery without relying on an in-memory count
#### Scenario: Concurrent eligibility requests occur
- **WHEN** two workers request the same limited package for the same player and period concurrently
- **THEN** a server-scoped uniqueness/idempotency constraint allows at most one request to reserve the remaining entitlement
#### Scenario: Delivery conclusively did not execute
- **WHEN** Run returns a conclusive failure proving that no gift effect occurred
- **THEN** Platform may release the reserved period entitlement transactionally; partial or unknown outcomes SHALL continue to hold it until reconciled
### Requirement: Frozen and idempotent gift delivery
The system SHALL freeze the target player, package/item definitions, plugin/game version, and delivery parameters before dispatch and SHALL use a stable server-scoped delivery ID and idempotency key.
#### Scenario: Package changes after delivery request
- **WHEN** an operator edits or disables a package after a delivery has been created
- **THEN** the existing delivery retains its frozen target/items/version and the edit does not mutate an in-flight or completed delivery
#### Scenario: Same idempotency key is submitted again
- **WHEN** the same scoped delivery request is repeated or submitted concurrently
- **THEN** Platform returns the original delivery and queues no duplicate effect
#### Scenario: Run receives a delivery
- **WHEN** a user with current effective `server.game-client.command` confirms an eligible frozen delivery
- **THEN** Run receives only the plugin-declared typed item aliases/quantities and stable delivery identity, not arbitrary browser commands
### Requirement: Conclusive delivery outcomes
The system SHALL record a gift as delivered only after conclusive Run execution evidence, and ambiguous or partial outcomes SHALL not trigger automatic whole-package redelivery.
#### Scenario: Job is queued or accepted
- **WHEN** Run has only claimed, acknowledged, or begun the delivery job
- **THEN** Platform records the delivery as in progress and SHALL NOT create a successful claim/receipt
#### Scenario: Delivery succeeds conclusively
- **WHEN** Run returns a schema-valid success receipt for every required item under the delivery idempotency key
- **THEN** Platform records the delivery as delivered exactly once
#### Scenario: Connection is lost after possible execution
- **WHEN** delivery may have executed but the result is missing, timed out, cancelled, or cannot be confirmed
- **THEN** Platform records an unknown outcome, performs confirmation before any explicit retry, and never automatically queues the entire delivery again
#### Scenario: Multi-item delivery is partial
- **WHEN** the transport cannot guarantee atomic delivery and only some item receipts are conclusive
- **THEN** Platform records per-item receipts and a partial/unknown package outcome and SHALL NOT label the whole package delivered or blindly redeliver confirmed items
#### Scenario: Notification fails after delivery
- **WHEN** all items are confirmed delivered but a post-delivery notification fails
- **THEN** Platform preserves the delivered fact, records the notification failure separately, and SHALL NOT redeliver the package
### Requirement: Gift management surface and history
The gift page SHALL provide statistics, a searchable/filterable paginated package table, package create/edit/detail/delete controls, a real player selector, a reviewed send dialog, and server-scoped delivery history without Workflow terminology.
#### Scenario: Operator opens gift management
- **WHEN** local gift data is available
- **THEN** the page shows package totals/enabled counts/delivery statistics, package contents and limits, and compact row actions without a hard-coded `starter-pack`, fixed player cards, or fixed notification text
#### Scenario: Operator reviews delivery result
- **WHEN** a delivery is in progress, delivered, failed, unknown, partial, or has a notification failure
- **THEN** the page shows the ordinary gift-delivery result and safe reason without exposing a Workflow instance, Workflow step, raw command, or audit-initiation action
@@ -0,0 +1,174 @@
## ADDED Requirements
### Requirement: Current-service schema compatibility gate
The system SHALL enable each SCUM database-backed capability only after the bound Run has performed a read-only probe against the current server database and the result matches a versioned plugin schema adapter for that capability.
#### Scenario: Supported current schema is detected
- **WHEN** the bound Run reports a schema fingerprint whose required tables, columns, types, joins, and cardinality match the plugin adapter for player reads
- **THEN** Platform enables the player-read capability for that server and records the adapter version, fingerprint, probe time, and safe capability result
#### Scenario: A current binding has no valid probe evidence
- **WHEN** a compatible Run binding becomes ready, reconnects, or reports a changed database/plugin identity without current probe evidence
- **THEN** Platform automatically schedules the bounded read-only probe before any affected query or write and exposes no browser action for starting it
#### Scenario: Generic probe execution is unavailable
- **WHEN** the bound Run lacks the required generic diagnostic-probe contract
- **THEN** database-backed SCUM capabilities remain disabled with a safe connection/capability reason and SHALL NOT fall back to a copied database, cached schema, reference model, or guessed query
#### Scenario: One world-data requirement is incompatible
- **WHEN** a required vehicle column is missing, has an incompatible type, or the declared join is not unique while player and squad requirements still match
- **THEN** Platform disables only the affected vehicle capability and SHALL NOT treat an empty vehicle list as a successful current-server result
#### Scenario: Compatibility evidence becomes invalid
- **WHEN** the game version, database instance identity, Run binding, plugin adapter version, or schema fingerprint changes
- **THEN** Platform invalidates the affected capability evidence and requires a new successful probe before another query or write uses that evidence
#### Scenario: Probe result is exposed safely
- **WHEN** Platform stores or returns schema compatibility status
- **THEN** the result excludes the database path, DSN, socket, credentials, raw SQL, raw row content, and host identity
#### Scenario: Run snapshots the active database for a safe read
- **WHEN** Run must use an execution-time read-only snapshot to avoid locking the active SQLite database
- **THEN** the snapshot is bound to the current server/Run/database identity, timestamped, checksummed, short-lived, and rejected after rebinding or source-identity change
### Requirement: Plugin-owned immutable parser and query assets
The SCUM plugin SHALL own versioned login parsers, SQLite query assets, and parameter/result schemas, and each executable data asset SHALL be referenced by an integrity digest in the plugin manifest and generated Run package.
#### Scenario: Declared query is dispatched
- **WHEN** Platform schedules the declared player query with schema-valid bounded parameters
- **THEN** Run executes the matching packaged asset only after the plugin ID, version, template key, server binding, and asset digest all match
#### Scenario: Caller supplies arbitrary query material
- **WHEN** a browser, AI request, Platform API caller, or job payload supplies raw SQL, an unknown template key, extra parameters, or a different asset digest
- **THEN** the request is rejected before SQLite execution and no protected query content is stored in a browser-visible response
#### Scenario: SCUM behavior is absent from Run
- **WHEN** Run selects an executor for a SCUM plugin query
- **THEN** it uses generic SQLite and packaged-asset contracts and contains no branch keyed by SCUM, a `scum.*` template name, a SCUM table, or a SCUM command
### Requirement: Bounded and correlated SQLite reads
Run SHALL execute plugin query assets with bound parameters and query-only restrictions, and Platform SHALL correlate every result to its original durable job before validating and storing it.
#### Scenario: Query violates the read boundary
- **WHEN** an asset or parameter expansion attempts multiple statements, DDL, mutation, `ATTACH`, extension loading, a write PRAGMA, or string-concatenated parameter injection
- **THEN** Run rejects execution with a stable safe error and returns no rows
#### Scenario: Query exceeds a declared bound
- **WHEN** execution times out, the database remains locked, or rows/result bytes exceed the template limits
- **THEN** Run stops the read and returns a bounded safe error without converting the failure into an empty successful result
#### Scenario: Fast, duplicate, or late result arrives
- **WHEN** a result arrives immediately, is delivered more than once, or arrives after a newer scan
- **THEN** the pre-existing job correlation prevents loss or cross-server association, duplicate delivery is idempotent, and an older result cannot replace a newer completed generation
#### Scenario: Result envelope is malformed
- **WHEN** the server/plugin binding, template key, schema version, asset digest, checksum, observed time, or row schema does not match the queued job
- **THEN** Platform rejects the result before any local SCUM record changes
### Requirement: Authentic login-driven player and session records
The system SHALL parse the plugin-declared current SCUM login/logout format and atomically maintain a player and session uniquely scoped by server instance and external player identifier.
#### Scenario: Parser version is enabled
- **WHEN** a plugin login parser is registered for a SCUM/game version
- **THEN** sanitized fixtures captured from the active service prove its successful login/logout mapping and parser digest before it can create durable players
#### Scenario: Unknown player logs in successfully
- **WHEN** an authentic successful login event contains a valid external player ID, display name, event identity, and occurrence time
- **THEN** Platform atomically creates or updates exactly one `(server_instance_id, external_player_id)` player and opens one matching local session
#### Scenario: Concurrent first-login delivery occurs
- **WHEN** two workers process the same new player's first successful login concurrently
- **THEN** a database unique constraint and idempotent event identity produce one player and one session without a duplicate-key user-visible failure
#### Scenario: Duplicate or older event arrives
- **WHEN** an acknowledged login/logout event is replayed or an older logout arrives after a later login
- **THEN** Platform creates no duplicate session and does not regress the player's current display name, last-seen time, or newer online session
#### Scenario: Source format has no native session identifier
- **WHEN** a valid login line has a transport cursor and player identity but no game-provided event/session ID
- **THEN** Platform derives a privacy-safe logical event fingerprint and deterministic server-scoped session identity from normalized non-network event fields, independent of stream generation, so replaying that logical line cannot create a second session
#### Scenario: Line is malformed or login did not succeed
- **WHEN** a partial, malformed, undecodable, or oversized line, failed authentication attempt, disconnect without a matching identity, or unrelated log message is received
- **THEN** the parser emits no successful-login event and Platform creates no player or fabricated session
#### Scenario: Logout has no matching session
- **WHEN** a valid logout event has no matching open session under the same server/player/source epoch
- **THEN** Platform records no fabricated login session and does not close a different or newer session
#### Scenario: Network material is present
- **WHEN** the source login line includes an IP address or another network identifier
- **THEN** raw network material is discarded before durable player/session storage and is absent from SCUM APIs
#### Scenario: Database save time changes
- **WHEN** a SCUM.db row reports `last_login_time`, `last_save_time`, or another persistence timestamp without a corresponding current login session event
- **THEN** Platform SHALL NOT mark the player online from that database timestamp alone
### Requirement: Login stream continuity and binding fences
The login source SHALL keep transport cursor identity separate from stable logical event identity and preserve current-binding isolation across batch replay, file rotation/truncation, Run restart, reconnect, and partial-line boundaries.
#### Scenario: Log file rotates or truncates
- **WHEN** the declared login file is replaced, rotated, or truncated
- **THEN** Run starts a new durable transport generation and resumes only at a complete-line boundary while any overlapped logical event retains the same privacy-safe fingerprint
#### Scenario: Rotation overlap replays an acknowledged login
- **WHEN** a previously acknowledged logical login line is observed again under a new stream generation during copy-truncate or rotation overlap
- **THEN** the new transport event is acknowledged idempotently and Platform creates no second player or session
#### Scenario: Run restarts with an acknowledged cursor
- **WHEN** Run restarts or reconnects after Platform acknowledged a source sequence
- **THEN** replay begins from a safe acknowledged boundary and duplicate batches remain idempotent
#### Scenario: Event belongs to an obsolete binding
- **WHEN** a typed login/logout event carries a Run binding, server instance, plugin version, stream generation, or source identity that no longer matches the active binding
- **THEN** Platform rejects it before changing a player or session
#### Scenario: Online session loses authoritative log continuity
- **WHEN** the binding is replaced, the server stops, or log continuity is lost without a matching logout event
- **THEN** Platform closes or marks the affected session unknown with a bounded terminal reason and SHALL NOT continue presenting it as a confirmed current login
### Requirement: Automatic server-bound synchronization
The system SHALL synchronize verified SCUM.db resources automatically when a compatible Run binding becomes ready and at plugin-declared bounded cadences, without a user-facing manual data-refresh operation.
#### Scenario: Compatible Run becomes ready
- **WHEN** a server obtains a current Run binding and successful capability probe
- **THEN** Platform schedules an initial bounded scan and later jittered resource scans within per-server concurrency limits
#### Scenario: New login needs enrichment
- **WHEN** login ingestion creates a player whose database details are not yet known
- **THEN** Platform may schedule a bounded player-detail lookup without delaying player creation or filling unknown values with defaults
#### Scenario: Page is opened
- **WHEN** an operator opens users, squads, map, or gifts
- **THEN** the page reads the platform-local data API and SHALL NOT dispatch a Run query, schema probe, projection refresh, audit, or synchronization command
#### Scenario: A later scan fails
- **WHEN** a resource previously synchronized successfully and a later scan fails or is partial
- **THEN** Platform retains the last completed local generation, records the safe connection/sync failure internally, and does not delete rows or present generated replacements
#### Scenario: Resource synchronization status is requested
- **WHEN** a local resource API reports its availability
- **THEN** it distinguishes not-yet-synchronized, successfully-synchronized-empty, schema-incompatible, current/last-complete data, and connection/read failure with concrete collection time and a safe reason
#### Scenario: No completed local generation exists
- **WHEN** a current scan has not completed successfully
- **THEN** Platform returns the appropriate unavailable/not-yet-synchronized state and SHALL NOT fall back to a legacy projection, sample record, reference database, cached unbound row, or fabricated empty success
### Requirement: Dedicated local SCUM database records
The system SHALL persist SCUM identities, sessions, details, squads, squad members, vehicles, flags, positions, gifts, deliveries, and sync cursors in dedicated queryable local stores rather than in SCUM projection/Workflow snapshot fields.
#### Scenario: File storage backend is active
- **WHEN** Platform runs with the default file metadata backend
- **THEN** SCUM records use a platform-owned local database under the configured data directory and a position update does not rewrite the global metadata snapshot
#### Scenario: MySQL storage backend is active
- **WHEN** Platform runs with the MySQL backend
- **THEN** explicit migrations create normalized SCUM tables with server-scoped indexes and uniqueness constraints instead of storing SCUM collections only inside `platform_metadata_snapshots.snapshot_json`
#### Scenario: Source omits a numeric fact
- **WHEN** a verified row omits fame, currency, a coordinate, an owner, or another fact
- **THEN** the corresponding local field remains null/absent and SHALL NOT be set to zero, an inferred owner, a generated coordinate, or an unrelated identifier
#### Scenario: Full scan completes
- **WHEN** every page of a bounded full resource scan passes schema and checksum validation
- **THEN** Platform transactionally commits that generation and may mark records absent only from that completed generation
@@ -0,0 +1,68 @@
## ADDED Requirements
### Requirement: Lean SCUM player record
The SCUM product SHALL maintain only server-scoped player identity, current display name, first/last seen times, online session/login history, and verified current-server player details required for management.
#### Scenario: Player changes display name
- **WHEN** a newer authentic login event reports a different display name for an existing server-scoped player
- **THEN** Platform updates the current display name and SHALL NOT create alias-intelligence, shared-IP, access-risk, or automatic-enforcement records
#### Scenario: SCUM player model is migrated
- **WHEN** the new player store becomes authoritative
- **THEN** SCUM page/API/manifest/log-ingestion flows no longer create, update, read, or expose player-intelligence, access-attempt, shared-fingerprint, security-signal, alias-history, projection, observation, or Workflow records; a shared non-SCUM domain may remain only for a proven non-SCUM consumer
### Requirement: Searchable and paginated player roster
The system SHALL provide a server-authorized local player-list API and full-width table with server-side pagination, bounded search, online-state and squad filters, and allowlisted sorting.
#### Scenario: Operator searches the roster
- **WHEN** an authorized operator searches by display name or external player ID and selects online or squad filters
- **THEN** Platform returns only matching players from the target server with total/page information and deterministic ordering
#### Scenario: Cross-server player identifier is supplied
- **WHEN** the same external player ID exists on another server or a filter references another server's squad
- **THEN** the response includes no cross-server row and reveals no other server association
#### Scenario: Unauthorized user requests players
- **WHEN** the session lacks effective read access to the target server
- **THEN** Platform denies the request without revealing whether any player exists
### Requirement: Trustworthy roster values
The player roster SHALL display current stored business fields without fabricating defaults and SHALL distinguish online-session evidence from database details.
#### Scenario: Verified player details exist
- **WHEN** local records contain verified fame, cash, gold, squad, and last activity facts
- **THEN** the roster shows player name, unique identifier, online state, those verified facts, and their last synchronized time
#### Scenario: Database details are not yet known
- **WHEN** a login-created player has no successful detail row yet
- **THEN** the roster shows the player identity/session with unknown detail fields and SHALL NOT substitute zero, `1`, sample data, or a guessed profile identifier
#### Scenario: No players have been recorded
- **WHEN** the local player table is confirmed empty
- **THEN** the page shows a normal player-data empty state without projection/Workflow terminology, sample players, or an action that starts data collection
### Requirement: Player detail and login history
The system SHALL provide a player detail surface containing server-scoped identity, current verified facts, current known coordinate, login history, and last synchronized/collected times.
#### Scenario: Operator opens player detail
- **WHEN** an authorized operator selects a roster row
- **THEN** a drawer, dialog, or detail route shows the player's known fields and bounded login history without exposing raw logs, IP addresses, database rows, SQL, XML, paths, or credentials
#### Scenario: Coordinate is unavailable
- **WHEN** no verified current coordinate exists for the player
- **THEN** the detail surface states that the coordinate is unavailable and SHALL NOT calculate or render a default map point
### Requirement: Explicit player edit form
The user-management surface SHALL edit player values through a named-field form that shows current and proposed values and delegates execution to the controlled-write capability.
#### Scenario: Operator edits an economy field
- **WHEN** an operator opens the Fame, cash, or gold edit control
- **THEN** the form requires an explicit target value and reason and SHALL NOT use hard-coded `+100`, `+1000`, or other fixed increment behavior
#### Scenario: Attribute current value is unknown
- **WHEN** an operator opens an attribute or `855` preset edit without verified current named values
- **THEN** the UI prevents submission and explains that current values must be known; it SHALL NOT default the before or after value to `0` or `1`
#### Scenario: User has read but not write access
- **WHEN** the current session may read players but lacks the effective permission for the selected field
- **THEN** the roster and detail remain available while the edit action is hidden or disabled with a readable permission explanation
@@ -0,0 +1,95 @@
## ADDED Requirements
### Requirement: Exact SCUM detail navigation
The SCUM server detail view SHALL contain exactly five tabs in this order: `用户管理`, `队伍管理`, `实时地图`, `礼包管理`, `AI 助手`.
#### Scenario: SCUM detail opens normally
- **WHEN** an operator opens a SCUM server detail without a valid section selector
- **THEN** the page selects `用户管理` as the default and renders no standalone `管理` or `Workflow 状态` tab
#### Scenario: Legacy section is requested
- **WHEN** a legacy `manage`, `workflows`, or otherwise invalid SCUM section is requested
- **THEN** the page replaces/falls back to `用户管理` without an empty screen or a Workflow/management placeholder
#### Scenario: Non-SCUM server detail is rendered
- **WHEN** another plugin/server type legitimately declares different navigation
- **THEN** the SCUM-only five-tab restriction does not silently remove that plugin's declared pages
### Requirement: Required management controls are relocated
Removing the SCUM `管理` tab SHALL NOT remove server deployment, metadata, or administrator-membership capabilities.
#### Scenario: Operator edits deployment
- **WHEN** an authorized operator needs to change the SCUM deployment definition
- **THEN** the existing server-list deployment action remains available and no replacement permanent detail tab is introduced
#### Scenario: Server owner opens settings
- **WHEN** the server owner selects the compact settings action in the detail header
- **THEN** a drawer or dialog permits display-name and administrator membership changes with the existing ownership authorization
#### Scenario: Non-owner opens server settings
- **WHEN** a server member who is not the owner views the settings surface
- **THEN** owner-only membership mutations are unavailable and a direct API attempt remains forbidden
### Requirement: Projection and Workflow concepts are removed from the product
SCUM pages and browser contracts SHALL NOT render or offer projection, observation, manual synchronization, audit-initiation, pending-review counter, or Workflow product concepts.
#### Scenario: Any retained SCUM page renders
- **WHEN** users, squads, map, gifts, or AI content is displayed
- **THEN** it contains none of `Workflow 状态`, `投影`, `真实投影`, `玩家投影`, `刷新投影`, `刷新世界投影`, `刷新真实数据`, `发起审计`, `创建发放 workflow`, `typed workflow`, `typed observation`, `typed operation`, `待审操作`, `审批/确认队列`, `清理旧入口`, `目前暂无真实投影数据`, `暂无真实投影数据`, `暂无玩家投影`, or `Companion 可用`
#### Scenario: Plugin page loads data
- **WHEN** any of the four SCUM management pages initializes or retries a failed local read
- **THEN** it calls only its local resource API and does not list/create Workflow instances/steps or start a query, projection refresh, real-data refresh, or audit
#### Scenario: Removed endpoint is requested
- **WHEN** a browser requests the removed SCUM Workflow, Workflow-step, observation, operation-list/approve, projection/real-data refresh, or audit-initiation endpoint
- **THEN** Platform returns not found or a stable removal response without dispatching a job or exposing a replacement raw execution path
#### Scenario: Legacy operation approval route is requested
- **WHEN** a caller invokes a legacy SCUM operation-list, `/scum/operations/{id}/approve`, pending-review, or confirmation-queue route
- **THEN** Platform returns not found or a stable removal response and SHALL NOT create an approval record, dispatch a write, or revive an approval product domain
#### Scenario: Internal write safety is recorded
- **WHEN** Platform records authorization, idempotency, backup, execution, or confirmation evidence for a write
- **THEN** the record remains internal/safe and does not create a user-facing audit-start action or Workflow status page
### Requirement: Trustworthy management page layouts
The user, squad, map, and gift pages SHALL use full-width business data layouts and shared theme-aware table, drawer, dialog, and status patterns without placeholder product data.
#### Scenario: Management page is implemented
- **WHEN** a first-party SCUM page adds or changes styles
- **THEN** it preserves black-mecha and magical-girl theme readability, reuses shared tokens/components, keeps CSS declarations compact, and does not add unrelated opaque SaaS cards or page-local fixed decorative effects
#### Scenario: Data load fails
- **WHEN** a local API request fails
- **THEN** the page shows a readable error and may retry only the local read; it does not generate sample players, squads, vehicles, flags, positions, gifts, maintenance evidence, backups, or operation results
### Requirement: AI assistant is preserved with reviewable effects
The `AI 助手` SHALL remain the final SCUM tab and SHALL support plugin-declared configuration suggestions and controlled player-operation drafts without receiving provider secrets or independent write authority.
#### Scenario: AI suggests plugin configuration
- **WHEN** an authorized operator requests SCUM plugin configuration assistance
- **THEN** AI proposes only plugin-declared fields, Platform validates and displays a reviewable diff, and the authorized review-and-confirm config-write path applies the change only after explicit confirmation
#### Scenario: User can view but cannot apply a suggestion
- **WHEN** the user may invoke/read an AI suggestion but lacks the permission required for the proposed config or player write
- **THEN** the suggestion remains reviewable while the apply action is disabled and backend application is forbidden
#### Scenario: AI provider is invoked
- **WHEN** Platform sends the allowed prompt/context to the configured AI provider
- **THEN** raw AI keys, database credentials, SCUM.db paths, raw XML, and protected Run material never reach the plugin page/browser
### Requirement: Obsolete UI actions and placeholders are removed
The SCUM product SHALL replace hard-coded actions and placeholder map/gift behavior with the declared business flows.
#### Scenario: Player actions render
- **WHEN** a player has writable verified fields
- **THEN** the UI offers explicit named-field editing and contains no `Fame +100`, `现金 +1000`, `855 审批`, fake before value, fake maintenance ID, or fake backup reference
#### Scenario: Map renders
- **WHEN** the map page is available
- **THEN** it uses the verified map capability and contains no gradient-only board or arbitrary percentage-dot transform
#### Scenario: Gift page renders
- **WHEN** gift management is available
- **THEN** it reads local gift packages and contains no hard-coded `starter-pack`, fixed player-card delivery, or fixed notification text
@@ -0,0 +1,69 @@
## ADDED Requirements
### Requirement: Independently verified current map facts
The system SHALL store and present current player, vehicle, flag, and squad-territory coordinates only when each resource's plugin adapter maps a compatible current-service source, identity, coordinate, and observation time.
#### Scenario: One position resource is incompatible
- **WHEN** player and flag positions are compatible but the vehicle query lacks a required column, type, identity, or unique join
- **THEN** Platform keeps the verified player/flag layers available, disables only the vehicle layer, and SHALL NOT return an empty vehicle list as a successful current result
#### Scenario: Vehicle ownership is unverified
- **WHEN** the source proves a vehicle identity/class and coordinate but does not prove a player or squad owner
- **THEN** Platform stores the verified fields and leaves ownership null instead of inferring it from nearby players, movement history, or an expiry heuristic
#### Scenario: Position scan later fails
- **WHEN** verified positions were previously stored and a later current-service scan fails or is partial
- **THEN** the map may retain the last completed rows with their concrete collection time and connection interruption, but SHALL NOT call them current or replace them with empty/generated points
### Requirement: Declared SCUM map asset and coordinate transform
The realtime map SHALL use a plugin-declared distributable map asset/version and integrity digest, verified world bounds, and a tested coordinate transform version/digest compatible with the current SCUM adapter.
#### Scenario: Verified coordinate is rendered
- **WHEN** a player, vehicle, flag, or territory coordinate falls within the declared bounds and transform version
- **THEN** the map places the marker at the tested map position and shows object type, name/ID, source coordinate, and collection time
#### Scenario: Map metadata is incompatible or absent
- **WHEN** the map asset, version, bounds, or transform does not match the current server adapter
- **THEN** the map shows a readable unavailable/error state and SHALL NOT fall back to a gradient board, arbitrary percentage transform, fake points, or sample routes
#### Scenario: Map asset digest does not match
- **WHEN** the packaged/browser-served map asset or transform contract fails its plugin-declared integrity check
- **THEN** the map is unavailable and SHALL NOT render coordinates against an unverified replacement asset
#### Scenario: Coordinate is outside bounds
- **WHEN** a source coordinate is non-finite or outside the declared world bounds
- **THEN** Platform rejects it from the current map dataset and records a bounded validation failure
### Requirement: Automatically updated and distinguishable map layers
The realtime map SHALL consume platform-local data automatically and provide distinguishable player, vehicle, and flag/squad-territory layers with player/squad filtering.
#### Scenario: Operator changes layer visibility
- **WHEN** an operator toggles players, vehicles, or flags/territory or applies a player/squad filter
- **THEN** the map updates the visible verified markers without dispatching a Run query or manual refresh operation
#### Scenario: Platform receives a newer coordinate
- **WHEN** a verified automatic sync or declared companion position event updates a current position
- **THEN** Platform publishes a safe local update and the open map can update through the platform event stream
#### Scenario: Source cadence is insufficient
- **WHEN** measured SCUM.db updates cannot support the declared realtime threshold
- **THEN** the plugin uses a declared verified companion position source or the feature presents the measured cadence honestly; it SHALL NOT increase unsafe polling or fabricate intermediate movement
#### Scenario: Map legend is rendered
- **WHEN** multiple entity types are visible
- **THEN** the UI provides icons or textual legend labels so entity meaning does not rely only on color
#### Scenario: Historical movement was not requested
- **WHEN** only current positions are available under this change
- **THEN** the product SHALL NOT fabricate trajectories, 24-hour trails, ride associations, sampling history, or playback controls to satisfy realtime-map acceptance
### Requirement: Server-isolated realtime-map API
The realtime-map API SHALL authorize the target server and return only bounded local current-position and safe object-summary fields needed by the map.
#### Scenario: Foreign map selector is supplied
- **WHEN** a player, squad, vehicle, flag, or territory selector belongs only to another server
- **THEN** Platform returns no foreign marker and does not reveal its existence or server identity
#### Scenario: Browser requests protected source material
- **WHEN** a browser attempts to request raw database rows, SQL, host paths, database identifiers, map-source credentials, or Run connection details
- **THEN** the API rejects the request and returns only named safe map fields
@@ -0,0 +1,53 @@
## ADDED Requirements
### Requirement: Independently gated squad, member, flag, and territory facts
The system SHALL store and present squads, squad members, leader/rank meaning, flags, territories, and their relationships only from current-service rows validated by the matching per-resource plugin adapter capability.
#### Scenario: Squad and member adapters are compatible
- **WHEN** squad and member queries complete successfully with verified identifiers, ranks, and player/profile relationships
- **THEN** Platform transactionally stores server-scoped squads and members and exposes only the leader/rank meaning declared by that adapter
#### Scenario: One squad resource is incompatible
- **WHEN** squad/member queries are compatible but flag ownership or territory coordinates are not
- **THEN** Platform keeps the verified squad/member capability available, disables only the incompatible resource, and SHALL NOT treat its absence as a verified empty list
#### Scenario: Leader meaning is unverified
- **WHEN** the current schema contains a rank value but the adapter cannot prove which value represents leader
- **THEN** Platform keeps leader unknown and SHALL NOT assume that a reference-project constant such as rank `4` is authoritative
#### Scenario: Flag ownership join is ambiguous
- **WHEN** a flag/base/profile relationship yields multiple possible squad owners or no verified squad link
- **THEN** Platform records no squad owner for that flag and exposes no inferred confidence label as fact
### Requirement: Searchable and paginated squad roster
The system SHALL provide a server-authorized local squad-list API and full-width table with bounded name/identifier search, server-side pagination, allowlisted sorting, and deterministic results.
#### Scenario: Operator searches squads
- **WHEN** an authorized operator searches or pages the target server's squads
- **THEN** Platform returns only matching server-local rows with total/page information, and the table shows squad ID, name, verified leader, member count, flag/territory status, known coordinate, and collection time
#### Scenario: No completed squad generation exists
- **WHEN** squads have not synchronized successfully or the squad adapter is incompatible
- **THEN** the page shows the corresponding not-yet-available/error state and SHALL NOT render reference, sample, projection, or generated squads
### Requirement: Semantic squad detail
The squad surface SHALL provide a drawer, dialog, or detail route that separates leader, members/ranks, flags, and territory instead of merging unrelated rows into one list.
#### Scenario: Operator opens a squad
- **WHEN** a server-authorized operator selects a squad
- **THEN** the detail shows only verified leader/member/rank and flag/territory facts with collection times and nullable unknown fields
#### Scenario: Verified territory coordinate is selected
- **WHEN** a squad or flag has a compatible verified coordinate and the realtime-map capability is available
- **THEN** the detail may navigate to that coordinate on the map without dispatching a new Run query or inventing a fallback point
### Requirement: Server-isolated squad APIs
Squad APIs SHALL authorize the target server and return only bounded local squad, member, flag, and territory fields needed by the management surface.
#### Scenario: Foreign selector is supplied
- **WHEN** a squad, player/member, flag, or territory selector belongs only to another server
- **THEN** Platform returns no foreign record and does not reveal its existence or server identity
#### Scenario: Browser requests protected source material
- **WHEN** a browser attempts to request raw SCUM.db rows, query text, host paths, database identifiers, or Run connection details
- **THEN** the API rejects the request and returns only named safe resource fields