neon-sprawl/docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md

82 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# E2.M2 — XpAwardAndLevelEngine
## Summary
| Field | Value |
|--------|--------|
| **Module ID** | E2.M2 |
| **Epic** | [Epic 2 — Skills and Progression Framework](../epics/epic_02_skills_and_progression.md) |
| **Stage target** | Prototype |
| **Status** | In Progress (see [dependency register](module_dependency_register.md)) |
## Implementation snapshot
**Linear (label `E2.M2`):** [Epic 2 project](https://linear.app/neon-sprawl/project/epic-2-classless-skill-and-progression-framework-5200f84bf8b6) — **Slice 2:** [NEO-37](https://linear.app/neon-sprawl/issue/NEO-37) (progression read snapshot) → [NEO-38](https://linear.app/neon-sprawl/issue/NEO-38) (apply grant + allowlist + level-up) → [NEO-39](https://linear.app/neon-sprawl/issue/NEO-39) (data-driven `LevelCurve` + CI); [NEO-40](https://linear.app/neon-sprawl/issue/NEO-40) (telemetry hook sites) after NEO-38, parallel with NEO-39. **Slice 3 integration** (multi-label where noted): [NEO-41](https://linear.app/neon-sprawl/issue/NEO-41), [NEO-42](https://linear.app/neon-sprawl/issue/NEO-42), [NEO-43](https://linear.app/neon-sprawl/issue/NEO-43), [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44).
**NEO-37 landed:** versioned **`GET /game/players/{id}/skill-progression`** ([implementation plan](../../plans/NEO-37-implementation-plan.md)) — `SkillProgressionSnapshotApi` / `SkillProgressionSnapshotDtos` under `server/NeonSprawl.Server/Game/Skills/`; one row per `ISkillDefinitionRegistry` skill; **`404`** when the player has no position (same gate as **`GET …/cooldown-snapshot`**). Manual QA **[`NEO-37.md`](../../manual-qa/NEO-37.md)**; **[server README — Skill progression snapshot (NEO-37)](../../../server/README.md#skill-progression-snapshot-neo-37)**.
**NEO-38 landed:** versioned **`POST /game/players/{id}/skill-progression`** ([implementation plan](../../plans/NEO-38-implementation-plan.md)) — applies one grant per request with **`ISkillDefinitionRegistry`** + **`allowedXpSourceKinds`** validation, stable deny **`reasonCode`** values, dual persistence (**`IPlayerSkillProgressionStore`**: in-memory / Postgres + [`V003__player_skill_progression.sql`](../../../server/db/migrations/V003__player_skill_progression.sql)), GET merges stored XP with the registry; success payload includes **`levelUps`** (see README for multi-step semantics). Bruno **`bruno/neon-sprawl-server/skill-progression/`** (GET + POST + deny); manual QA **[`NEO-38.md`](../../manual-qa/NEO-38.md)**; **[server README — Skill progression grant (NEO-38)](../../../server/README.md#skill-progression-grant-neo-38)**.
**NEO-39 landed:** data-driven **`LevelCurve`** at `content/skills/prototype_level_curve.json` with schema `content/schemas/level-curve.schema.json`; CI validation extended in `scripts/validate_content.py`; server boot now fail-fast loads curve content and uses it for XP→level resolution in skill progression GET/POST paths (no code edit required for threshold tuning).
**NEO-40 landed:** comment-only **telemetry hook sites** in `SkillProgressionSnapshotApi` (`POST …/skill-progression`) for future catalog events **`xp_grant`** and **`level_up`** ([implementation plan](../../plans/NEO-40-implementation-plan.md)); manual QA **[`NEO-40.md`](../../manual-qa/NEO-40.md)**.
**NEO-41 landed:** prototype **gather → skill XP** — successful **`POST …/interact`** when interactable **`kind`** is **`resource_node`** grants **`salvage`** XP (**`sourceKind: activity`**, **10** XP per interact) via shared **`SkillProgressionGrantOperations`** ([implementation plan](../../plans/NEO-41-implementation-plan.md)); manual QA **[`NEO-41.md`](../../manual-qa/NEO-41.md)**; **[server README — Interaction (gather)](../../../server/README.md#interaction-neo-9)**.
**NEO-42 landed (prep):** **`RefineActivitySkillXpGrant`** + **`RefineSkillXpConstants`** + test **`RefineActivityDeniedRegistryWebApplicationFactory`** — craft/refine success is intended to award **`refine`** XP (**`sourceKind: activity`**, **10** XP) via the same **`SkillProgressionGrantOperations`** stack as NEO-38 ([implementation plan](../../plans/NEO-42-implementation-plan.md)); manual QA **[`NEO-42.md`](../../manual-qa/NEO-42.md)**; **[server README — Craft / refine hook (NEO-42)](../../../server/README.md#craft--refine-hook--skill-xp-neo-42)**. **E3.M2** must invoke **`GrantOnSuccessfulCraftOrRefine`** on real **`CraftResult`** success for end-to-end behavior (no craft route in repo at this slice).
**NEO-43 landed (prep):** **`MissionRewardSkillXpGrant`** for scripted **`mission_reward`** skill XP ([implementation plan](../../plans/NEO-43-implementation-plan.md)); manual QA **[`NEO-43.md`](../../manual-qa/NEO-43.md)**; full quest hand-in awaits **E7.M2**.
**NEO-44 landed (E5.M1 / combat path, not E2.M2 skill grant):** combat defeat on **`AbilityCastApi`** accept when **`targetDefeated`** grants **25** gig XP to prototype main gig **`breach`** via **`CombatDefeatGigXpGrant`** + **`IPlayerGigProgressionStore`** ([NEO-44 plan](../../plans/NEO-44-implementation-plan.md)); read model **`GET /game/players/{id}/gig-progression`**; manual QA **[`NEO-44.md`](../../manual-qa/NEO-44.md)**; **[server README — Gig progression (NEO-44)](../../../server/README.md#gig-progression-snapshot-neo-44)**; module handoff [E5.M1](E5_M1_CombatRulesEngine.md).
**Still backlog within E2.M2 / Slice 3:** **NEO-42** end-to-end craft wiring awaits **E3.M2** success handler (prep helper landed).
**E2.M3:** [MasteryAndPerkUnlocks](E2_M3_MasteryAndPerkUnlocks.md) re-evaluates perk eligibility on skill **level-up** after grants ([NEO-45](https://linear.app/neon-sprawl/issue/NEO-45) → [NEO-49](https://linear.app/neon-sprawl/issue/NEO-49)).
Keep this paragraph and [documentation tracking](documentation_and_implementation_alignment.md) in sync as stories merge.
## Purpose
Central server-authoritative engine for **non-combat skill** progression: applying XP grants, resolving levels against data-driven curves, and emitting level-up events. **Gathering** and **crafting** integrate as callers for **skill** XP. **Combat encounters** advance **gigs** (gig XP), not `SkillDef` lines. **Mission rewards** may call this API for **skill** XP when the payout lists a skill grant. Callers are not hard dependencies for implementing the engine core (see Epic 2 Slice 2 vs Slice 3).
## Responsibilities
- Apply `XpGrantEvent` (or equivalent) per skill with validated **`skillId`** from [E2.M1](E2_M1_SkillDefinitionRegistry.md). Reject grants when **`sourceKind`** is not in the target `SkillDef.allowedXpSourceKinds`.
- Resolve experience against `LevelCurve` / threshold tables.
- Emit `LevelUpEvent` when thresholds are crossed.
- Support data-driven curves; **reload policy:** [Data reload and telemetry operations policy](data_and_ops_policy.md#content-and-gameplay-data-reload) (boot load default; optional dev reload).
## Key contracts
| Contract | Role |
|----------|------|
| `XpGrantEvent` | Structured XP award: **`skillId`** (must exist in registry), **amount**, **`sourceKind`** (must appear in that skills **`allowedXpSourceKinds`**), optional modifiers. |
| `LevelCurve` | Thresholds or formula per skill or global policy. |
| `LevelUpEvent` | Notification for UI, unlocks, and downstream systems. |
## Module dependencies
- **E2.M1** — [SkillDefinitionRegistry](E2_M1_SkillDefinitionRegistry.md): `SkillDef`, categories, **allowedXpSourceKinds**, and validation of skill IDs.
## Dependents (by design)
- **E5.M1** — CombatRulesEngine: **gig XP** from combat (not `SkillDef` XP via E2.M2).
- **E3.M1 / E3.M2** — Gathering and crafting award XP when wired in Slice 3.
- **E7.M2** — RewardAndUnlockRouter routes quest rewards including XP through E2.M2.
- **E2.M3 / E2.M4** — Mastery and pacing layers build on level/XP state (pre-production).
## Related implementation slices
- **Epic 2 Slice 2 — XP engine (E2.M2 core)**: implement contracts and tests without requiring Epic 3 or 5 modules.
- **Epic 2 Slice 3 — XP award integration**: E3.M1 / E3.M2 call the skill XP API with **`sourceKind`: `activity`** (where allowed per skill); E7.M2 / mission rewards use **`mission_reward`** (etc.) as configured; combat resolves **gig** progression separately from E2.M2.
## Risks and telemetry
- Runaway leveling in one skill: diminishing returns per slice; monitor distribution via E9.M2 when available.
- Telemetry: `xp_grant`, `level_up`, time-to-first-level-up once real activity fires.
## Source anchors
- Master plan: [`neon_sprawl_vision.plan.md`](../../../neon_sprawl_vision.plan.md) — Core Epic Map, Epic 2.
- [Module dependency register](module_dependency_register.md)