NEO-24: add manual QA checklist
First instance of the new docs/manual-qa/ convention. Covers boot sync, Tab happy path, out-of-range soft-lock (both by Tab and by locomotion per Decision 2), swap-to-beta, Esc clear, denial UI hygiene, input behavior, move-rejection coexistence, transport failure, and no- regression checks against NEO-7/9/19/22.pull/47/head
parent
f87ff2c99a
commit
cbcb4e3d63
|
|
@ -0,0 +1,105 @@
|
|||
# NEO-24 — Manual QA checklist
|
||||
|
||||
| Field | Value |
|
||||
|--------|--------|
|
||||
| **Key** | NEO-24 |
|
||||
| **Title** | E1.M3: Client tab-target + lock UI synced to server |
|
||||
| **Linear** | [NEO-24](https://linear.app/neon-sprawl/issue/NEO-24/e1m3-client-tab-target-lock-ui-synced-to-server) |
|
||||
| **Plan** | [`docs/plans/NEO-24-implementation-plan.md`](../plans/NEO-24-implementation-plan.md) |
|
||||
| **Branch** | `NEO-24-e1m3-client-tab-target-lock-ui-synced-to-server` |
|
||||
|
||||
Pair the client (Godot **F5**) with the server (`cd server/NeonSprawl.Server && dotnet run`). Keep the server console visible so you can see route hits, and the Godot **Output** dock visible for client warnings.
|
||||
|
||||
## 0. Setup sanity
|
||||
|
||||
- [ ] Server prints a URL around `http://localhost:5253` on boot; `GET /health` returns JSON.
|
||||
- [ ] In Godot, select `TargetSelectionClient` in the scene tree: **`base_url`** matches the server URL and **`dev_player_id`** matches `Game:DevPlayerId` (default `dev-local-1`).
|
||||
- [ ] (Optional) Bruno collection `bruno/neon-sprawl-server/targeting/` is configured for the same port — use it to cross-check server state after client actions.
|
||||
|
||||
## 1. Boot state
|
||||
|
||||
- [ ] On run, the player snaps to spawn (`-5, 0.9, -5`) — existing NEO-7 behavior, unchanged.
|
||||
- [ ] `UICanvas/TargetLockLabel` (top-left, under `PlayerPositionLabel`) shows:
|
||||
- `Target: —`
|
||||
- `Validity: none`
|
||||
- `Seq: 0`
|
||||
- [ ] Godot **Output** has no `TargetSelectionClient:` warnings. Server console shows a single `GET /game/players/dev-local-1/target` from the boot sync.
|
||||
|
||||
## 2. Tab cycle — happy path
|
||||
|
||||
- [ ] Press **Tab**. HUD flips to:
|
||||
- `Target: prototype_target_alpha`
|
||||
- `Validity: ok`
|
||||
- `Seq: 1`
|
||||
- [ ] Server log shows `POST /game/players/dev-local-1/target/select` with `{"schemaVersion":1,"targetId":"prototype_target_alpha"}`.
|
||||
- [ ] No `Denied:` line is appended to the HUD (success ⇒ no `reasonCode`).
|
||||
|
||||
## 3. Tab to out-of-range target — soft lock preserved
|
||||
|
||||
- [ ] With alpha locked, press **Tab** again. The server denies the swap to beta (spawn is ~18 m from `(8, 8)`; beta radius is 4 m).
|
||||
- [ ] HUD **stays** at `Target: prototype_target_alpha`, `Validity: ok`, `Seq: 1` with **`Denied: out_of_range`** appended. That is the NEO-23 soft-lock rule: a failed swap does not change the persisted lock or its sequence.
|
||||
- [ ] Server log shows the denial: `POST` → `200` with `selectionApplied: false`.
|
||||
- [ ] Press **Tab** several more times from here. Each press cleanly denies; HUD never drifts or shows a `beta` / optimistic lock between responses.
|
||||
|
||||
## 4. Soft-lock out-of-range via locomotion (plan Decision 2)
|
||||
|
||||
- [ ] Still locked to alpha, **WASD** away from spawn until you are ~8 m horizontally from `(-5, -5)`. Within ~250 ms of the next authority snap, HUD flips to:
|
||||
- `Target: prototype_target_alpha` (unchanged — soft lock)
|
||||
- `Validity: out_of_range`
|
||||
- `Seq: 1` (unchanged — no lock id change)
|
||||
- [ ] Walk back inside the radius. HUD returns to `Validity: ok` within ~250 ms of the next snap.
|
||||
- [ ] While walking, Godot Output is **not** spammed with targeting GETs. Server log shows no more than roughly one targeting `GET` per 250 ms during sustained motion.
|
||||
- [ ] Stand still with no lock changes. Server log shows **no** targeting GETs at idle (no timer polling).
|
||||
|
||||
## 5. Swap to beta when in range
|
||||
|
||||
- [ ] WASD toward beta at **(8, 8)** until you are within 4 m. Press **Tab**. HUD swaps cleanly:
|
||||
- `Target: prototype_target_beta`
|
||||
- `Validity: ok`
|
||||
- `Seq` increments by 1
|
||||
- [ ] Press **Tab** once more. Server denies (alpha is far away) — HUD stays at `beta / ok` with `Denied: out_of_range` appended.
|
||||
|
||||
## 6. Clear
|
||||
|
||||
- [ ] Press **Esc** at any point with a lock held. HUD flips to:
|
||||
- `Target: —`
|
||||
- `Validity: none`
|
||||
- `Seq` increments by 1
|
||||
- [ ] Any `Denied: …` line from the previous state is gone.
|
||||
- [ ] Server log shows `POST /target/select` with body `{"schemaVersion":1}` (no `targetId` key).
|
||||
- [ ] Press **Esc** again with no lock. Server returns the same cleared state; HUD stays `—` / `none` (server may or may not bump sequence — whatever it returns is what the HUD shows).
|
||||
|
||||
## 7. Denial UI hygiene
|
||||
|
||||
- [ ] Press **Tab** from `—` while far from both targets. If the server denies (possible depending on exact spawn distance), HUD shows `Target: —`, `Validity: none`, `Denied: out_of_range`. The lock id does **not** get "stuck" at something the server refused.
|
||||
- [ ] After any successful Tab / Esc, the `Denied: …` line goes away on the next response.
|
||||
|
||||
## 8. Input behavior
|
||||
|
||||
- [ ] **Tab** does **not** make focus jump around UI during play (we intercept in `_input` before UI focus navigation).
|
||||
- [ ] **Esc** does not quit or minimize the game.
|
||||
- [ ] Holding **Tab** does not auto-repeat into a firehose of POSTs (pressed action only fires on key press, not echo).
|
||||
- [ ] **E** (NEO-9 interact) and **WASD** (NEO-22 move) still work independently of the target lock; having a lock does not block interact or movement.
|
||||
|
||||
## 9. Coexistence with movement rejection (NEO-19 / move-stream 400)
|
||||
|
||||
- [ ] Walk at the orange `MoveRejectPedestal` (~(7.5, −6.5)) while holding a lock. When `move-stream` returns **400** (`vertical_step_exceeded`), `PositionAuthorityClient` runs a boot-style re-sync which emits `authoritative_position_received`.
|
||||
- [ ] HUD's `MoveRejectLabel` shows the rejection message as before (NEO-19).
|
||||
- [ ] `TargetSelectionClient` treats that snap like any other: if a lock is held, it fires at most one refresh GET (subject to cooldown). No duplicate requests, no stuck busy state.
|
||||
|
||||
## 10. Server-down / transport failure
|
||||
|
||||
- [ ] Stop the server. Press **Tab** / **Esc**. Godot **Output** prints `TargetSelectionClient: HTTP failed …` warnings; the client is not wedged — you can press again (busy flag is released on failure).
|
||||
- [ ] Restart the server and confirm the next **Tab** succeeds; HUD updates from the response.
|
||||
|
||||
## 11. No regressions
|
||||
|
||||
- [ ] Boot snap still works (NEO-7).
|
||||
- [ ] WASD locomotion + `move-stream` still works (NEO-22); `PlayerPositionLabel` updates every physics tick.
|
||||
- [ ] `E` interact with the prototype terminal still prints `allowed=true/false` in Output (NEO-9).
|
||||
- [ ] Camera zoom / occluder obstacle toggle (Ctrl+Shift+K) still work.
|
||||
- [ ] No new **red** lines in Godot Output during a typical 1–2 minute session. Benign warnings from other systems are fine; nothing from `TargetSelectionClient` during normal play.
|
||||
|
||||
---
|
||||
|
||||
When sections 1–10 are ticked and 11 shows no regressions, the story is ready for **In Test** / PR. Record any deviations or follow-ups in the **Decisions** or **Open questions** sections of the [implementation plan](../plans/NEO-24-implementation-plan.md).
|
||||
Loading…
Reference in New Issue