NEO-42: add implementation plan for craft/refine skill XP

pull/77/head
VinPropane 2026-05-10 19:36:04 -04:00
parent 49f67ecf4c
commit 20a141ba37
1 changed files with 77 additions and 0 deletions

View File

@ -0,0 +1,77 @@
# NEO-42 — Implementation plan
## Story reference
| Field | Value |
|--------|--------|
| **Key** | NEO-42 |
| **Title** | Craft / refine awards skill XP (sourceKind: activity) |
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-42/craft-refine-awards-skill-xp-sourcekind-activity |
| **Labels** | E3.M2, E2.M2, server |
| **Blocked by (Linear)** | [NEO-38](https://linear.app/neon-sprawl/issue/NEO-38) (grant path — **done** on `main`); **E3.M2** recipe/refine execution (not present in repo at kickoff). |
## Kickoff clarifications
| Topic | Question / note | Resolution |
|--------|-----------------|------------|
| **Delivery shape** | User asked what to recommend until E3.M2 HTTP/recipe loop exists. | **Agent recommendation (accepted unless you object in chat):** add **`RefineSkillXpConstants`** + a thin **`RefineActivitySkillXpGrant`** (or equivalent) that delegates to **`SkillProgressionGrantOperations.TryApplyGrant`** — **no** new public craft HTTP in NEO-42. E3.M2 calls this helper from the real **`CraftResult`** success path when that module lands. |
| **Prototype XP amount** | User asked what to recommend. | **Agent recommendation:** **10** XP per successful craft/refine completion for the prototype, matching **[NEO-41](NEO-41-implementation-plan.md)** gather constant pattern; replace with recipe-driven amounts in a later issue when content exists. |
| **“Craft” vs “refine” skill id** | `content/skills/prototype_skills.json` defines **`refine`** (process) with **`activity`** in `allowedXpSourceKinds`; there is no separate **`craft`** skill row today. | **Prototype mapping:** both **recipe craft** and **refine** success paths call the helper with **`skillId: refine`** and **`sourceKind: activity`** so allowlist enforcement matches catalog. If a dedicated craft skill is added later, E3.M2 can call the same helper with a different `skillId` per recipe metadata. |
## Goal, scope, and out-of-scope
**Goal:** When **E3.M2** reports a **successful** craft or refine completion, award **refine** skill XP through the **NEO-38** grant stack (`TryApplyGrant` → registry allowlist → store → level curve) with **`sourceKind: activity`** wherever **`activity`** is listed on the target skills **`allowedXpSourceKinds`**.
**In scope (from Linear):**
- Wire (or **prepare the single call site** for) craft/refine completion → **`SkillProgressionGrantOperations.TryApplyGrant`** with **`activity`**.
- **Respect catalog allowlists** (no grant when `activity` is disallowed — mirror NEO-41 defensive tests).
**Out of scope:**
- Owning the full **E3.M2** recipe validation, inventory mutations, or new public craft routes (separate story/module).
- **Recipe-driven XP amounts** and multi-skill mapping from **`RecipeDef`** (follow-up once execution + content exist).
- **Telemetry** (`NEO-40`) — comment-only hook sites already noted on grant path.
## Acceptance criteria checklist
- [ ] Craft/refine completion grants skill XP via **NEO-38** path (`TryApplyGrant` / shared persistence as `POST …/skill-progression`).
- [ ] Grant channels respect **skill catalog** `allowedXpSourceKinds` (deny / no-op when `activity` not allowed).
## Technical approach
1. **Constants** — Add `RefineSkillXpConstants` (parallel to `GatherSkillXpConstants`): **`refine`** skill id, **`activity`** source kind, **10** XP per completion (name clearly scoped to prototype craft/refine success).
2. **Shared grant helper** — Add a small static type (e.g. `RefineActivitySkillXpGrant`) whose sole job is to invoke **`SkillProgressionGrantOperations.TryApplyGrant`** with those constants. **Discard** the outcome on success paths intended to mirror gather (UI reads **`GET …/skill-progression`**); callers that need structured denies can call **`TryApplyGrant`** directly later.
3. **E3.M2 integration** — When **`CraftResult`** (or equivalent) success is finalized in server code, add **one call** to the helper with `playerId` + injected `ISkillDefinitionRegistry`, `IPlayerSkillProgressionStore`, `ISkillLevelCurve`. If E3.M2 is still absent when NEO-42 implements (1)(2), document the **exact line / type** in this plans **Decisions** after the merge that adds the success handler.
4. **Tests** — (a) Happy path: grant applies **refine** XP with **`activity`** when catalog allows it. (b) Deny path: stub registry where **`refine`** disallows **`activity`** → helper run leaves **0** XP (same spirit as `SalvageActivityDeniedRegistryWebApplicationFactory` in NEO-41). Use **in-memory** store + real or test JSON catalog + placeholder level curve; **no** dependency on craft HTTP.
5. **Manual QA** — If only (1)(2) ship before E3.M2: **`docs/manual-qa/NEO-42.md`** can note “verify via unit tests + temporary `POST …/skill-progression` control grant for `refine`/`activity`”; once E3.M2 exists, extend checklist to **Bruno** craft success + **GET** progression.
## Files to add
| Path | Purpose |
|------|---------|
| `server/NeonSprawl.Server/Game/Skills/RefineSkillXpConstants.cs` | NEO-42 prototype constants: `refine`, `activity`, flat XP amount. |
| `server/NeonSprawl.Server/Game/Skills/RefineActivitySkillXpGrant.cs` | Single entry point calling `TryApplyGrant` for successful craft/refine (prototype). |
| `server/NeonSprawl.Server.Tests/Game/Skills/RefineActivitySkillXpGrantTests.cs` | AAA tests: grant when allowed; no XP when `activity` not in allowlist for `refine`. |
| `docs/manual-qa/NEO-42.md` | Checklist during implementation per repo convention (may be minimal until E3.M2 endpoint exists). |
## Files to modify
| Path | Rationale |
|------|-----------|
| **None at kickoff** for application wiring — **E3.M2** success handler file path is unknown until that module exists; add the **`RefineActivitySkillXpGrant`** call there in the same or follow-up PR. If E3.M2 lands before NEO-42 implementation, replace this row with the concrete path (e.g. `…/Crafting/CraftApi.cs`). |
## Tests
| Test file | What it covers |
|-----------|------------------|
| `RefineActivitySkillXpGrantTests.cs` | **Arrange / Act / Assert:** successful grant increases **`refine`** XP via shared path; registry without **`activity`** for **`refine`** yields **no** XP delta (allowlist respected). |
## Open questions / risks
- **Merge ordering:** If NEO-42 merges **before** E3.M2, acceptance checkbox “craft/refine completion” is only fully satisfied after a tiny follow-up commit adds the call in the new success handler — track in Linear or a child issue if needed.
- **XP balance:** Flat **10** is a placeholder; design may change before public playtests.