neon-sprawl/docs/plans/E2M3-pre-production-backlog.md

165 lines
7.0 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.M3 — Pre-production story backlog (MasteryAndPerkUnlocks)
Working backlog for **Epic 2 — Slice 4** ([mastery and pacing](../decomposition/epics/epic_02_skills_and_progression.md#slice-4---mastery-and-pacing-pre-production)). Decomposition and contracts: [E2.M3 — MasteryAndPerkUnlocks](../decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md).
**Labels (Linear):** every issue **`E2.M3`**; add **`server`** / **`Feature`** as listed per issue.
**Precursor (do not re-scope):** skill **level** authority lives in **E2.M2** ([E2.M2 module](../decomposition/modules/E2_M2_XpAwardAndLevelEngine.md)); `SkillDef` ids from **E2.M1**. **Gig** perks are **not** `SkillDef` mastery.
**Flagship skill:** **`salvage`** (gather loop — [NEO-41](https://linear.app/neon-sprawl/issue/NEO-41)).
**Linear issues (created):** attach `docs/plans/NEO-*-implementation-plan.md` on the same branch as implementation work.
| Slug | Linear |
|------|--------|
| E2M3-01 | [NEO-45](https://linear.app/neon-sprawl/issue/NEO-45/e2m3-lock-prototype-salvage-mastery-catalog-schemas-ci) |
| E2M3-02 | [NEO-46](https://linear.app/neon-sprawl/issue/NEO-46/e2m3-server-loads-mastery-catalog-at-startup-fail-fast) |
| E2M3-03 | [NEO-47](https://linear.app/neon-sprawl/issue/NEO-47/e2m3-perk-unlock-engine-perkstate-persistence) |
| E2M3-04 | [NEO-48](https://linear.app/neon-sprawl/issue/NEO-48/e2m3-perk-state-get-branch-selection-post-bruno) |
| E2M3-05 | [NEO-49](https://linear.app/neon-sprawl/issue/NEO-49/e2m3-perk-unlock-telemetry-hook-sites) |
**Dependency graph in Linear:** E2M3-02 blocked by E2M3-01. E2M3-03 blocked by E2M3-02 and [NEO-38](https://linear.app/neon-sprawl/issue/NEO-38). E2M3-04 blocked by E2M3-03. E2M3-05 blocked by E2M3-03 (may parallel E2M3-04).
---
## Story order (recommended)
| Order | Slug | Depends on |
|-------|------|------------|
| 1 | **E2M3-01** | E2.M1 `salvage` `SkillDef` ([NEO-33](https://linear.app/neon-sprawl/issue/NEO-33)) |
| 2 | **E2M3-02** | E2M3-01 |
| 3 | **E2M3-03** | E2M3-02, [NEO-38](https://linear.app/neon-sprawl/issue/NEO-38) (skill level) |
| 4 | **E2M3-04** | E2M3-03 |
| 5 | **E2M3-05** | E2M3-03 (parallel OK with E2M3-04) |
---
### E2M3-01 — Prototype salvage mastery catalog + schemas + CI
**Goal:** Lock content shape and CI validation for one **`salvage`** `MasteryTrack` before any server load.
**In scope**
- `content/schemas/mastery-catalog.schema.json` (or equivalent single-catalog schema).
- `content/mastery/prototype_salvage_mastery.json` with tier 1 @ level 2 (`scrap_efficiency` vs `bulk_haul`) and tier 2 @ level 4 perks.
- `scripts/validate_content.py` checks: unknown `skillId`, duplicate perk ids, branch integrity.
- Module doc designer note (mutually exclusive branches; no respec v1).
**Out of scope**
- Server loader, perk state, HTTP, client HUD.
**Acceptance criteria**
- [x] PR gate validates mastery JSON against schema. (**Done** — NEO-45.)
- [x] Only **`salvage`** has a track; `refine` / `intrusion` not required. (**Done** — NEO-45.)
- [x] Stable `branchId` / `perkId` documented in [E2_M3](../decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md) freeze box. (**Done** — NEO-45.)
---
### E2M3-02 — Server mastery catalog load (fail-fast)
**Goal:** Disk → host: startup load of mastery catalog(s) with cross-check against `ISkillDefinitionRegistry`.
**NEO-46 kickoff:** mirror CI gate constants from [NEO-45 implementation plan](NEO-45-implementation-plan.md) (see E2.M3 **NEO-46 handoff**).
**In scope**
- Loader + `IMasteryCatalogRegistry` under `server/NeonSprawl.Server/Game/Skills/` (or `Game/Mastery/` if split).
- Fail-fast on malformed files, duplicate ids, unknown `skillId`.
- Unit tests (AAA) for loader happy path and failure modes.
**Out of scope**
- Per-player state, unlock logic, HTTP.
**Acceptance criteria**
- [ ] Server refuses boot when catalog invalid (mirror E2.M1 catalog behavior).
- [ ] Registry resolves track by `skillId` and perk metadata by `perkId`.
---
### E2M3-03 — Perk unlock engine + PerkState persistence
**Goal:** Server-authoritative unlock evaluation from **skill level** + branch selection; persist `PerkState`; re-run on level-up.
**In scope**
- `IPlayerPerkStateStore` (in-memory + Postgres + migration), mirroring skill progression stores.
- Unlock engine: tier eligibility, mutually exclusive branch pick, `PerkUnlockEvent` emission.
- Hook after `SkillProgressionGrantOperations` (or dedicated service) on level-up for affected `skillId`.
- Integration/unit tests (AAA): eligibility, select branch, deny invalid pick.
**Out of scope**
- HTTP API, Bruno, client UI.
- Applying `effectKind` in gather/craft/combat.
**Acceptance criteria**
- [ ] Player at salvage level 2 can select one tier-1 branch; other branchs tier-1 perks stay locked.
- [ ] Level 4 unlocks tier-2 perks on the chosen branch path only.
- [ ] Stable `reasonCode` on deny (unknown tier, branch already chosen, level too low, etc.).
---
### E2M3-04 — Perk state HTTP + Bruno
**Goal:** Versioned **GET** snapshot and **POST** branch selection for manual QA and future client.
**In scope**
- `GET /game/players/{id}/perk-state` (path finalized in implementation plan).
- `POST` branch selection endpoint with structured success/deny payloads.
- Bruno `bruno/neon-sprawl-server/perk-state/`.
- `server/README.md` section; API tests (AAA).
**Out of scope**
- Godot HUD — decompose per [full-stack epic decomposition](../../.cursor/rules/full-stack-epic-decomposition.md) (planned **E2 client slice**; skill/perk UI not covered by E3 Slice 5 alone).
- Gameplay stat application.
**Acceptance criteria**
- [ ] GET returns unlocked perks + branch picks for prototype salvage track.
- [ ] POST select branch persists and matches engine rules from E2M3-03.
- [ ] Bruno exercises happy path + at least one deny case.
---
### E2M3-05 — `perk_unlock` telemetry hook sites
**Goal:** Comment-only hooks in unlock / branch-select path for future E9.M1 catalog event **`perk_unlock`**.
**In scope**
- Hook placement in unlock engine (and POST path if applicable).
- `TODO(E9.M1)` comments; no production logging.
- README + module doc pointer.
**Out of scope**
- Telemetry ingest, dashboards.
**Acceptance criteria**
- [ ] Hook sites documented in code and `server/README.md`.
- [ ] Matches Epic 2 Slice 4 telemetry vocabulary.
---
## After this backlog
- Track delivery in Linear; keep `blockedBy` links synchronized if scope changes.
- For each issue kickoff, add `docs/plans/{NEO-XX}-implementation-plan.md` per [story-kickoff](../../.cursor/rules/story-kickoff.md).
- Add `docs/manual-qa/{NEO-XX}.md` when HTTP surfaces land (E2M3-04+).
## Related docs
- [E2_M3_MasteryAndPerkUnlocks.md](../decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md)
- [E2_M2_XpAwardAndLevelEngine.md](../decomposition/modules/E2_M2_XpAwardAndLevelEngine.md)
- [E2_M1_SkillDefinitionRegistry.md](../decomposition/modules/E2_M1_SkillDefinitionRegistry.md)
- [epic_02_skills_and_progression.md](../decomposition/epics/epic_02_skills_and_progression.md)
- [skills.md](../game-design/skills.md)