# OpenSpec Change: enterprise-gaming-progression - **Change ID**: enterprise-gaming-progression - **Package**: `@suaveplan/progression` - **Category**: gaming/narrative - **Target path**: `gaming/narrative/progression` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/progression.md` --- ## Summary Deliver `@suaveplan/progression` on the genesis branch from scratch at enterprise quality. The package provides character progression with XP curves, skill trees, perks, and achievements. A `ProgressionManager` accumulates XP and triggers level-up events; an `XpCurve` computes required XP per level from configurable formulas (polynomial, exponential, flat table); a `SkillTree` represents an acyclic graph of `SkillNode` objects with prerequisite unlocking; a `PerkRegistry` holds passive modifiers unlockable at level milestones; an `AchievementTracker` monitors player statistics and fires unlock events. Genesis adds Zod schemas, structured errors, OTel XP counter, injectable clock, and 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:^`) | `ProgressionError`; `PROG.*` codes | | `@suaveplan/types` (`workspace:^`) | `Clock` | | `@suaveplan/events` (`workspace:^`) | `prog:level_up`, `prog:skill_unlocked`, `prog:perk_unlocked`, `prog:achievement` | | `zod` (`catalog:forms`) | Skill tree + achievement schemas | | `@opentelemetry/api` (`catalog:observability`) | XP counter, level gauge | --- ## Public API surface ```ts interface XpCurveOptions { formula: 'polynomial' | 'exponential' | 'table' base?: number coefficient?: number exponent?: number table?: number[] // XP required per level (table formula) } function createXpCurve(options: XpCurveOptions): XpCurve interface XpCurve { xpForLevel(level: number): number levelForXp(xp: number): number } function createProgressionManager(options: ProgressionManagerOptions): ProgressionManager interface ProgressionManagerOptions { curve: XpCurve maxLevel?: number clock?: Clock } interface ProgressionManager { awardXp(playerId: string, amount: number, source?: string): void getLevel(playerId: string): number getXp(playerId: string): number getProgress(playerId: string): number // 0–1 toward next level } // Skill tree interface SkillNode { id: string name: string description: string cost: number // skill points required prerequisites?: string[] effects?: SkillEffect[] } interface SkillEffect { stat: string modifier: number type: 'add' | 'multiply' } function createSkillTree(nodes: SkillNode[]): SkillTree interface SkillTree { canUnlock(nodeId: string, unlockedIds: Set, availablePoints: number): boolean unlock(nodeId: string, unlockedIds: Set, availablePoints: number): Set computeEffects(unlockedIds: Set): Map } // Achievements interface AchievementDefinition { id: string title: string description: string condition: { stat: string; threshold: number; op: '>=' | '<=' | '==' } } function createAchievementTracker(definitions: AchievementDefinition[]): AchievementTracker interface AchievementTracker { updateStat(stat: string, value: number): void incrementStat(stat: string, by?: number): void getUnlocked(): string[] isUnlocked(id: string): boolean } ``` ### Error codes ```ts type ProgErrorCode = | 'PROG_PLAYER_NOT_FOUND' | 'PROG_SKILL_NOT_FOUND' | 'PROG_PREREQUISITES_NOT_MET' | 'PROG_INSUFFICIENT_POINTS' | 'PROG_MAX_LEVEL_REACHED' ``` --- ## File layout ```text gaming/narrative/progression/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/index.ts types/common.ts types/common.md core/ xp-curve/ xp-curve.ts xp-curve.md xp-curve.types.ts xp-curve.test.ts index.ts progression-manager/ progression-manager.ts progression-manager.md progression-manager.types.ts progression-manager.test.ts index.ts skill-tree/ skill-tree.ts skill-tree.md skill-tree.types.ts skill-tree.test.ts index.ts achievement-tracker/ achievement-tracker.ts achievement-tracker.md achievement-tracker.types.ts achievement-tracker.test.ts index.ts tests/ integration.test.ts ``` --- ## Impact **Downstream**: `@suaveplan/economy` reads player level for pricing; `@suaveplan/react-game-hud` shows XP bar. Prerequisites: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`.