6.9 KiB
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 (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 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 skill’s allowedXpSourceKinds.
In scope (from Linear):
- Wire (or prepare the single call site for) craft/refine completion →
SkillProgressionGrantOperations.TryApplyGrantwithactivity. - Respect catalog allowlists (no grant when
activityis 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
- Preparatory:
RefineActivitySkillXpGrantinvokes the NEO-38 path (TryApplyGrant, same stack asPOST …/skill-progression). Follow-up: E3.M2 success handler must callGrantOnSuccessfulCraftOrRefine(documented inserver/README.md+ manual QA); no craft route in repo yet. - Grant channels respect skill catalog
allowedXpSourceKinds(stub registry test:refinewithoutactivity→ no XP).
Decisions (implementation)
RefineActivityDeniedRegistryWebApplicationFactoryadded for allowlist-deny coverage (parallel to NEO-41 salvage-denied factory).
Technical approach
-
Constants — Add
RefineSkillXpConstants(parallel toGatherSkillXpConstants):refineskill id,activitysource kind, 10 XP per completion (name clearly scoped to prototype craft/refine success). -
Shared grant helper — Add a small static type (e.g.
RefineActivitySkillXpGrant) whose sole job is to invokeSkillProgressionGrantOperations.TryApplyGrantwith those constants. Discard the outcome on success paths intended to mirror gather (UI readsGET …/skill-progression); callers that need structured denies can callTryApplyGrantdirectly later. -
E3.M2 integration — When
CraftResult(or equivalent) success is finalized in server code, add one call to the helper withplayerId+ injectedISkillDefinitionRegistry,IPlayerSkillProgressionStore,ISkillLevelCurve. If E3.M2 is still absent when NEO-42 implements (1)–(2), document the exact line / type in this plan’s Decisions after the merge that adds the success handler. -
Tests — (a) Happy path: grant applies refine XP with
activitywhen catalog allows it. (b) Deny path: stub registry whererefinedisallowsactivity→ helper run leaves 0 XP (same spirit asSalvageActivityDeniedRegistryWebApplicationFactoryin NEO-41). Use in-memory store + real or test JSON catalog + placeholder level curve; no dependency on craft HTTP. -
Manual QA — If only (1)–(2) ship before E3.M2:
docs/manual-qa/NEO-42.mdcan note “verify via unit tests + temporaryPOST …/skill-progressioncontrol grant forrefine/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. |
server/NeonSprawl.Server.Tests/Game/Skills/RefineActivityDeniedRegistryWebApplicationFactory.cs |
Test host with refine catalog row that omits activity. |
docs/manual-qa/NEO-42.md |
Curl checklist + E3.M2 follow-up note. |
Files to modify
| Path | Rationale |
|---|---|
server/README.md |
Document NEO-42 hook type and E3.M2 integration expectation. |
| E3.M2 craft success handler (path TBD) | Call RefineActivitySkillXpGrant.GrantOnSuccessfulCraftOrRefine once on successful craft/refine — not done in this PR (module absent). |
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.