# NEO-31 — Manual QA checklist | Field | Value | |-------|-------| | Key | NEO-31 | | Title | E1M4-02: Input to AbilityCastRequest path | | Linear | https://linear.app/neon-sprawl/issue/NEO-31/e1m4-02-input-to-abilitycastrequest-path | | Plan | `docs/plans/NEO-31-implementation-plan.md` | | Branch | `NEO-31-e1m4-02-input-to-abilitycastrequest-path` | ## 1) Server + Bruno - [ ] Start server: `cd server/NeonSprawl.Server && dotnet run`. - [ ] `curl -s http://localhost:5253/health` returns JSON with `status: ok`. - [ ] Run Bruno collection (or at least): `ability-cast/Post hotbar bind slot0 pulse` (seq **20**), **`ability-cast/Post target select alpha before cast`** (seq **21**), then **`ability-cast/Post cast happy`** (seq **22**) — expect cast `accepted: true`. - [ ] `ability-cast/Post cast bad schema` → HTTP **400**. - [ ] `ability-cast/Post cast missing player` → HTTP **404**. - [ ] **`ability-cast/Post cast loadout mismatch`** (seq **25**): run **`ability-cast/Post hotbar bind slot0 pulse`** in the same session first so slot 0 is `prototype_pulse`; then expect `accepted: false` and `reasonCode == "loadout_mismatch"`. ## 2) Client — bound slot - [ ] With server running, open main scene; confirm hotbar hydrated (NEO-29). - [ ] Bind slot **0** to `prototype_pulse` (Bruno `hotbar-loadout/Post loadout bind` or equivalent `curl`) if not already. - [ ] Press **1** (InputMap `hotbar_slot_1`): Godot Output shows a line starting with **`ability_cast_requested`** including `slot=0`, `ability_id=prototype_pulse`, and `targetId=` matching current HUD lock (`null` / `—` when cleared, or the locked id after Tab). - [ ] Server log or network trace shows **one** `POST …/ability-cast` per key press (no spam while key held; Godot `is_action_pressed` fires once per press). ## 3) Client — unbound slot - [ ] Ensure slot **2** has no ability (default empty). - [ ] Press **3** (`hotbar_slot_3`): **no** cast POST; Output shows **`Hotbar cast ignored: slot 2 empty_slot`** (or equivalent `push_warning` text). ## 4) Deny UX - [ ] Force a server deny (e.g. cast wrong ability for bound slot via `curl`): response `accepted: false`; if exercised through the client, Output includes **`ability_cast_denied`** in a warning.