E2.M3 — MasteryAndPerkUnlocks
Summary
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.
| Order |
Issue |
Summary |
| 1 |
NEO-45 |
Lock prototype salvage mastery catalog + schemas + CI |
| 2 |
NEO-46 |
Server loads mastery catalog at startup (fail-fast) |
| 3 |
NEO-47 |
Perk unlock engine + PerkState persistence |
| 4 |
NEO-48 |
Perk state GET + branch selection POST + Bruno |
| 5 |
NEO-49 |
perk_unlock telemetry hook sites |
docs/plans/NEO-*-implementation-plan.md files are added at story kickoff (per planning-implementation-docs).
Purpose
Mastery tracks and perk unlock state so skill builds have expressive identity beyond raw level curves from E2.M2. Gig mastery/perks remain a separate system (docs/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) |
| 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)
MasteryTrack fields (prototype):
| Field |
Rule |
skillId |
Must exist in E2.M1 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)
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
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).
- 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 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); align with E9.M1 when catalog exists.
Source anchors