Files
suaveplan/openspec/changes/enterprise-gaming-game-persistence/proposal.md
2026-05-27 23:29:53 +01:00

4.4 KiB

OpenSpec Change: enterprise-gaming-game-persistence

  • Change ID: enterprise-gaming-game-persistence
  • Package: @suaveplan/game-persistence
  • Category: gaming/engine
  • Target path: gaming/engine/game-persistence
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/game-persistence.md

Summary

Deliver @suaveplan/game-persistence on the genesis branch from scratch at enterprise quality. The package provides save/load game state with versioned migrations — a universal persistence substrate for both client-side saves and server-side session snapshots. A SaveManager holds named slots per player, auto-increments version on re-save, enforces configurable slot-count and size limits, and fires @suaveplan/events lifecycle events. A MigrationRunner upgrades older saves through a registered migration chain. Genesis adds Zod schemas, @suaveplan/serialize for binary/JSON round-trips, OTel instrumentation, structured errors, and full 100% coverage.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) GamePersistenceError; SAVE.* error codes
@suaveplan/types (workspace:^) StorageLike, Clock
@suaveplan/events (workspace:^) Save lifecycle events
@suaveplan/serialize (workspace:^) Tagged JSON + MessagePack round-trip for save data
zod (catalog:forms) SaveSlot, MigrationConfig schemas
@opentelemetry/api (catalog:observability) Save/load span instrumentation

Public API surface

function createSaveManager(options: SaveManagerOptions): SaveManager

interface SaveManagerOptions {
  maxSlotsPerPlayer?: number     // default 10
  maxSizeBytes?: number          // default 1MB per slot
  autosaveSlot?: string          // default 'autosave'
  storage?: StorageLike          // pluggable; default: in-memory Map
  clock?: Clock
  migrations?: Migration[]
}

interface SaveManager {
  save(playerId: string, slotId: string, data: unknown): Promise<SaveSlot>
  load(playerId: string, slotId: string): Promise<SaveSlot>
  delete(playerId: string, slotId: string): Promise<void>
  listSlots(playerId: string): Promise<SaveSlot[]>
  autosave(playerId: string, data: unknown): Promise<SaveSlot>
  hasSlot(playerId: string, slotId: string): Promise<boolean>
}

interface SaveSlot {
  id: string
  playerId: string
  version: number
  createdAt: number
  updatedAt: number
  sizeBytes: number
  data: unknown
}

// Migration
interface Migration {
  fromVersion: number
  toVersion: number
  migrate(data: unknown): unknown
}

function createMigrationRunner(migrations: Migration[]): MigrationRunner

interface MigrationRunner {
  run(data: unknown, fromVersion: number, toVersion: number): unknown
  canMigrate(fromVersion: number, toVersion: number): boolean
}

Error codes

type SaveErrorCode =
  | 'SAVE_SLOT_NOT_FOUND'
  | 'SAVE_SLOT_LIMIT_EXCEEDED'
  | 'SAVE_SIZE_EXCEEDED'
  | 'SAVE_MIGRATION_FAILED'
  | 'SAVE_SERIALIZATION_FAILED'
  | 'SAVE_STORAGE_ERROR'

New additions for genesis

  1. Binary serialization — @suaveplan/serialize MessagePack profile for compact save blobs; JSON fallback.
  2. Migration chain validation — MigrationRunner validates no gaps or cycles in version sequence at construction time.
  3. OTel — save.write_duration_ms and save.read_duration_ms histograms; save.slot_count gauge.
  4. Events — save:created, save:updated, save:loaded, save:deleted, save:autosave, save:migration_applied.

File layout

gaming/engine/game-persistence/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    core/
      save-manager/
        save-manager.ts
        save-manager.md
        save-manager.types.ts
        save-manager.test.ts
        index.ts
      migration-runner/
        migration-runner.ts
        migration-runner.md
        migration-runner.types.ts
        migration-runner.test.ts
        index.ts
  tests/
    integration.test.ts

Impact

Downstream: @suaveplan/api-plugin-game-persistence wraps this in HTTP routes. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/serialize.