# E2.M3 — MasteryAndPerkUnlocks ## Summary | Field | Value | |--------|--------| | **Module ID** | E2.M3 | | **Epic** | [Epic 2 — Skills and Progression Framework](../epics/epic_02_skills_and_progression.md) | | **Linear (Epic 2 project)** | [Epic 2 — Classless Skill and Progression Framework](https://linear.app/neon-sprawl/project/epic-2-classless-skill-and-progression-framework-5200f84bf8b6) | | **Stage target** | Pre-production | | **Status** | Planned (see [dependency register](module_dependency_register.md)) | ## Linear backlog (Epic 2 Slice 4 — E2.M3) Issues use label **`E2.M3`**. Dependency order: **NEO-45 → NEO-46 → NEO-47 → NEO-48**; **NEO-49** after **NEO-47** (may parallel **NEO-48**). Canonical story text: [E2M3-pre-production-backlog.md](../../plans/E2M3-pre-production-backlog.md). | Order | Issue | Summary | |------:|-------|---------| | 1 | [NEO-45](https://linear.app/neon-sprawl/issue/NEO-45/e2m3-lock-prototype-salvage-mastery-catalog-schemas-ci) | Lock prototype **salvage** mastery catalog + schemas + CI | | 2 | [NEO-46](https://linear.app/neon-sprawl/issue/NEO-46/e2m3-server-loads-mastery-catalog-at-startup-fail-fast) | Server loads mastery catalog at startup (fail-fast) | | 3 | [NEO-47](https://linear.app/neon-sprawl/issue/NEO-47/e2m3-perk-unlock-engine-perkstate-persistence) | Perk unlock engine + `PerkState` persistence | | 4 | [NEO-48](https://linear.app/neon-sprawl/issue/NEO-48/e2m3-perk-state-get-branch-selection-post-bruno) | Perk state GET + branch selection POST + Bruno | | 5 | [NEO-49](https://linear.app/neon-sprawl/issue/NEO-49/e2m3-perk-unlock-telemetry-hook-sites) | `perk_unlock` telemetry hook sites | `docs/plans/NEO-*-implementation-plan.md` files are added at story kickoff (per [planning-implementation-docs](../../../.cursor/rules/planning-implementation-docs.md)). ## Purpose Mastery tracks and perk unlock state so **skill** builds have expressive identity beyond raw level curves from [E2.M2](E2_M2_XpAwardAndLevelEngine.md). **Gig** mastery/perks remain a **separate** system ([`docs/game-design/gigs.md`](../../game-design/gigs.md)). ## v1 design decisions (frozen for backlog) | Topic | v1 choice | |-------|-----------| | Progress into mastery | **Skill level** from E2.M2 (`IPlayerSkillProgressionStore`) — no separate mastery XP bar | | Branch model | One **`MasteryTrack`** per skill; **tiers** with **mutually exclusive** branch picks per tier | | Respec | **Out of scope** (late-stage tuning per [`skills.md`](../../game-design/skills.md)) | | Gig perks | **No** shared tree with gigs | | Gameplay effects | **State + data only** in E2.M3 core; stat/yield hooks are optional follow-ups (e.g. E3.M1) | ## Data contract (schema) | Artifact | Path | |----------|------| | **JSON Schema** | [`content/schemas/mastery-catalog.schema.json`](../../../content/schemas/mastery-catalog.schema.json) | | **Prototype catalog** | [`content/mastery/prototype_salvage_mastery.json`](../../../content/mastery/prototype_salvage_mastery.json) | **`MasteryTrack` fields (prototype):** | Field | Rule | |--------|------| | **`skillId`** | Must exist in [E2.M1](E2_M1_SkillDefinitionRegistry.md) `SkillDef` catalog (`salvage` for flagship slice). | | **`tiers`** | Ordered list; each tier has **`requiredLevel`** (skill level from E2.M2) and **`branches`**. | | **`branches`** | Stable **`branchId`**; **mutually exclusive** pick per tier; each branch lists **`perkIds`** unlocked when chosen. | **`PerkDef` fields (prototype, embedded or referenced in catalog):** | Field | Rule | |--------|------| | **`id`** | Lowercase `snake_case`, **immutable** after ship. | | **`displayName`** | Player-facing; may change without migrating unlock state. | | **`effectKind`** | Optional stub string for future systems (gather yield, craft efficiency, etc.) — **no** gameplay apply in E2.M3 core stories. | ### Designer note: tiers, branches, and level gates - **Level gates** come from E2.M2 only: when a player’s **skill level** reaches **`requiredLevel`**, that tier’s branches become **eligible** for selection (server-authoritative). - **One branch per tier:** picking `scrap_efficiency` forecloses `bulk_haul` for that tier; perks on the chosen branch unlock; the other branch’s perks for that tier do **not** unlock. - **Other skills** (`refine`, `intrusion`) have **no** mastery rows in the prototype catalog until a deliberate expansion issue relaxes CI. - **Respec** is not implemented in v1 — document in content if a perk is “permanent for the character.” ## Prototype Slice 4 freeze — salvage flagship ([NEO-45](https://linear.app/neon-sprawl/issue/NEO-45)) The **first shipped mastery track** is **`salvage`** only: | Tier | `requiredLevel` | Branches (pick one) | Tier-2 `perkId` (chosen branch only) | |------|-----------------|---------------------|--------------------------------------| | 1 | 2 | `scrap_efficiency` · `bulk_haul` | *(none — branch pick only)* | | 2 | 4 | same branch ids | `salvage_scrap_efficiency_1` · `salvage_bulk_haul_1` | **Rules:** Do **not** rename `branchId` / `perkId` values to “fix” wording—change `displayName` only, or add new ids and migrate content. Expanding mastery to **`refine`** / **`intrusion`** belongs in a new Linear issue once the catalog gate intentionally grows. ## Responsibilities - Load and validate mastery catalog from data; expose track/perk metadata via `IMasteryCatalogRegistry` (name flexible). - Persist **`PerkState`** per player: unlocked **`perkId`** set + chosen **`branchId`** per tier. - Evaluate eligibility from **skill level**; apply branch selection; emit **`PerkUnlockEvent`** for telemetry and future hooks. - Re-evaluate eligibility on **skill level-up** (after E2.M2 grants). - Expose versioned HTTP read/write for perk state (prototype JSON v1). ## Key contracts | Contract | Kind | Role | |----------|------|------| | `MasteryTrack` | Content | Per-skill tier/branch/perk tree. | | `PerkDef` | Content | Stable perk metadata (+ optional `effectKind` stub). | | `PerkState` | Server-internal + HTTP DTO | Unlocked perks + branch picks per tier. | | `PerkUnlockEvent` | Server-internal | Fired when a perk becomes unlocked (UI/telemetry/craft gates later). | ## Module dependencies - **E2.M2** — [XpAwardAndLevelEngine](E2_M2_XpAwardAndLevelEngine.md): skill **level** source for tier gates; level-up hook site for re-evaluation. - **E2.M1** — [SkillDefinitionRegistry](E2_M1_SkillDefinitionRegistry.md): validate catalog `skillId` references. ## Dependents (by design) - **E3.M1 / E3.M2** — Optional gates or modifiers on gather/craft when perk `effectKind` is wired (follow-up integration, not E2.M3 core). - **E7.M2** — Mission rewards may reference perk prerequisites in content (future). - Gameplay systems that gate recipes or abilities on **`perkId`** (integration per content). ## Acceptance criteria (E2.M3 / Slice 4) - At least one **salvage** mastery path unlocks a perk **without** blocking progression in **`refine`** or **`intrusion`** (separate skills / tracks). - Branch pick at tier 1 is **mutually exclusive**; server denies invalid or duplicate picks with stable **`reasonCode`** values. - Catalog + perk state validated in CI or at server boot ([CT.M1](CT_M1_ContentValidationPipeline.md)). - Telemetry hook sites documented for **`perk_unlock`** (no E9.M1 ingest until catalog exists). ## Related implementation slices - **Epic 2 Slice 4** — E2.M3 (this module) + [E2.M4](E2_M4_ProgressionPacingControls.md) pacing (separate backlog; depends on E9.M2). - **Epic 2 Slice 3** — skill XP integration remains on E2.M2; **NEO-44** (gig XP) is Epic 5, not E2.M3. ## Risks and telemetry - **Risk:** Mastery feels mandatory for one skill and blocks alts — **Mitigation:** flagship is one skill; other prototype skills remain level-only until expanded. - **Risk:** Branch picks without respec frustrate players — **Mitigation:** defer respec to late-stage tuning; keep v1 tree small. - **Telemetry:** `perk_unlock` hook sites ([NEO-49](https://linear.app/neon-sprawl/issue/NEO-49)); align with [E9.M1](E9_M1_TelemetryEventSchema.md) when catalog exists. ## Source anchors - Master plan: [`neon_sprawl_vision.plan.md`](../../../neon_sprawl_vision.plan.md) — Epic 2, E2.M3. - Game design: [`docs/game-design/skills.md`](../../game-design/skills.md) — Mastery and perks. - [Module dependency register](module_dependency_register.md)