# Manual QA — NEO-43 (mission / quest → skill XP, `mission_reward`) Reference: [implementation plan](../plans/NEO-43-implementation-plan.md), [NEO-38](./NEO-38.md) (skill progression grant), [NEO-42](./NEO-42.md) (prep-hook analogue). ## Preconditions - Run `NeonSprawl.Server` from `server/NeonSprawl.Server` (`dotnet run`; default `http://localhost:5253`). - Player **`dev-local-1`** (default seed). ```bash BASE=http://localhost:5253 ID=dev-local-1 ``` ## Automated coverage `MissionRewardSkillXpGrantTests` exercises the **NEO-38** grant path with disk **`prototype_skills.json`** (`salvage` allows **`mission_reward`**) and a stub registry where **`salvage`** omits **`mission_reward`** from **`allowedXpSourceKinds`**. ## Control: direct POST grant (same stack as E7.M2 hook) Move once so **`GET …/skill-progression`** is not **404**: ```bash curl -sS -i -X POST "${BASE}/game/players/${ID}/move" \ -H "Content-Type: application/json" \ -d '{"schemaVersion":1,"target":{"x":0.5,"y":0.9,"z":0.5}}' ``` Baseline: ```bash curl -sS "${BASE}/game/players/${ID}/skill-progression" ``` Apply **`salvage`** + **`mission_reward`** (mirrors what **`MissionRewardSkillXpGrant.GrantFromMissionReward`** does internally for that skill/amount): ```bash curl -sS -i -X POST "${BASE}/game/players/${ID}/skill-progression" \ -H "Content-Type: application/json" \ -d '{"schemaVersion":1,"skillId":"salvage","amount":25,"sourceKind":"mission_reward"}' ``` Expect **HTTP 200**, **`granted": true`**, and **`salvage`** **`xp`** increased (repeat **POST** to see cumulative XP). ## Deny path (catalog guard) With default catalog, **`trainer`** is **not** allowed for **`salvage`**: ```bash curl -sS -i -X POST "${BASE}/game/players/${ID}/skill-progression" \ -H "Content-Type: application/json" \ -d '{"schemaVersion":1,"skillId":"salvage","amount":5,"sourceKind":"trainer"}' ``` Expect **HTTP 200**, **`granted": false`**, **`reasonCode":"source_kind_not_allowed"`**. ## Combat vs mission XP **Skill XP** from missions uses **`mission_reward`** (this story). **Combat encounters** must not grant skill XP by default — they award **gig XP** separately; see [progression.md](../game-design/progression.md) (*Mission rewards vs combat XP*). There is no encounter reward implementation under `server/` yet; when **E5 / E7** adds encounter payouts, keep skill progression grants off the default encounter pipe unless a product exception is explicitly modeled. ## E7.M2 follow-up When quest hand-in / **`QuestRewardBundle`** apply exists, confirm **one** server-side call to **`MissionRewardSkillXpGrant.GrantFromMissionReward`** per scripted skill XP line item (with content-driven **`skillId`** and **`amount`**), then **`GET …/skill-progression`** shows expected totals.