neon-sprawl/docs/plans/NEO-42-implementation-plan.md

6.9 KiB
Raw Permalink Blame History

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.TryApplyGrantno 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 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

  • Preparatory: RefineActivitySkillXpGrant invokes the NEO-38 path (TryApplyGrant, same stack as POST …/skill-progression). Follow-up: E3.M2 success handler must call GrantOnSuccessfulCraftOrRefine (documented in server/README.md + manual QA); no craft route in repo yet.
  • Grant channels respect skill catalog allowedXpSourceKinds (stub registry test: refine without activity → no XP).

Decisions (implementation)

  • RefineActivityDeniedRegistryWebApplicationFactory added for allowlist-deny coverage (parallel to NEO-41 salvage-denied factory).

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.
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.