From e900ba992bbb3c7da37a6039b467518806111a9b Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 31 May 2026 20:02:14 -0400 Subject: [PATCH] NEO-111: add capstone manual QA script and README loop section. Single-session Godot checklist with flexible defeat order, inventory verification, and Godot-restart idempotency; cross-link from NEO-110. --- client/README.md | 23 ++++++++++ docs/manual-qa/NEO-110.md | 2 +- docs/manual-qa/NEO-111.md | 90 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 docs/manual-qa/NEO-111.md diff --git a/client/README.md b/client/README.md index 6f2bf55..9a3dd03 100644 --- a/client/README.md +++ b/client/README.md @@ -225,6 +225,29 @@ Full capstone checklist: [`docs/manual-qa/NEO-86.md`](../docs/manual-qa/NEO-86.m Full checklist: [`docs/manual-qa/NEO-110.md`](../docs/manual-qa/NEO-110.md). +## End-to-end encounter clear loop (NEO-111) + +Epic 5 Slice 3 capstone — defeat all three prototype NPCs, receive loot + quest token **once**, verify inventory **in Godot** without Bruno. + +**Flow:** fresh server restart → boot encounter HUD **0/3** → defeat ×3 (any order) → progress **1/3 → 2/3 → completed** → loot label + inventory → Godot restart idempotency. + +| Step | Input / trigger | HUD / server outcome | +|------|-----------------|----------------------| +| Boot | Godot **F5** + fresh server | **`EncounterProgressLabel`** **`not started (0/3)`**; **`Loot: —`**; empty bag | +| Engage | **Tab** + **1** on first NPC | Encounter activates on first damaging hit (NEO-106); progress **`1/3`** after defeat | +| Second defeat | Lock + cast second NPC | **`2/3`**; loot still **`—`** | +| Third defeat | Lock + cast third NPC | **`completed (3/3)`**; **`EncounterCompleteLabel`** lists grants; **`InventoryLabel`** auto-refreshes | +| Idempotency | Stop Godot + **F5** (server still running) | Encounter still **`completed`**; inventory counts unchanged | +| Re-cast defeated | **Tab** + **1** on defeated NPC | **`ability_cast_denied: target_defeated`**; no duplicate loot or gig XP | + +**Cross-links:** [NEO-106](../docs/plans/NEO-106-implementation-plan.md) combat wiring · [NEO-108](../docs/plans/NEO-108-implementation-plan.md) encounter-progress GET · [NEO-110](../docs/manual-qa/NEO-110.md) HUD components · [NEO-98](../docs/manual-qa/NEO-98.md) NPC combat · [NEO-86](../docs/manual-qa/NEO-86.md) gig XP per defeat. + +**Scripts:** `encounter_progress_client.gd`, `inventory_client.gd`, `ability_cast_client.gd`, `combat_targets_client.gd` — wired from `main.gd`. + +**Preconditions:** **Server restart** before capstone run resets NPC HP, encounter progress, inventory, and gig XP. + +Full capstone checklist: [`docs/manual-qa/NEO-111.md`](../docs/manual-qa/NEO-111.md). + ## Inventory snapshot HUD (NEO-72) - **`GET /game/players/{id}/inventory`** — server-authoritative bag (**24** slots) + equipment stub (**1** slot); see [server README — Player inventory](../server/README.md#player-inventory-neo-54-store-neo-55-http). diff --git a/docs/manual-qa/NEO-110.md b/docs/manual-qa/NEO-110.md index 5565c42..1164e13 100644 --- a/docs/manual-qa/NEO-110.md +++ b/docs/manual-qa/NEO-110.md @@ -49,7 +49,7 @@ Player casts **`prototype_pulse`** (**25** damage, ~**3 s** cooldown). - Defeat order may differ from table — server tracks order-independent subset; count is **`defeatedTargetIds.size()`**. - Capstone baseline prefers **server restart** over mid-session fixture POST. -- Full three-NPC loop + idempotency capstone: [NEO-111](NEO-111.md) (when landed). +- Full three-NPC loop + idempotency capstone: [NEO-111 manual QA](NEO-111.md). - Component combat regression: [NEO-98 manual QA](NEO-98.md). ## Acceptance diff --git a/docs/manual-qa/NEO-111.md b/docs/manual-qa/NEO-111.md new file mode 100644 index 0000000..96b3656 --- /dev/null +++ b/docs/manual-qa/NEO-111.md @@ -0,0 +1,90 @@ +# NEO-111 — Manual QA checklist + +| Field | Value | +|-------|-------| +| Key | NEO-111 | +| Title | E5M3-12: Playable encounter clear capstone (Godot) | +| Linear | https://linear.app/neon-sprawl/issue/NEO-111/e5m3-12-playable-encounter-clear-capstone-godot | +| Plan | `docs/plans/NEO-111-implementation-plan.md` | +| Branch | `NEO-111-playable-encounter-clear-capstone` | + +## Preconditions + +- **Fresh dev player:** stop any running server, then start a new instance so in-memory **`dev-local-1`** NPC HP, encounter progress, inventory, and gig XP reset. +- **No Bruno/curl** for this checklist — defeat chain uses Godot **Tab** + **1** only. +- NEO-110 encounter HUD, NEO-106 combat wiring, and NEO-108 encounter-progress GET landed on `main`. +- **`Economy HUD`** toggle **on** for inventory verification. + +## Archetype reference (defeat order flexible) + +| Archetype | Instance id | Marker | Pulses to defeat | +|-----------|-------------|--------|------------------| +| Melee | `prototype_npc_melee` | orange west **`(-3, -3)`** | 4 | +| Ranged | `prototype_npc_ranged` | purple SE **`(3, 3)`** | 4 | +| Elite | `prototype_npc_elite` | gold origin **`(0, 0)`** | 8 | + +Player casts **`prototype_pulse`** (**25** damage, ~**3 s** cooldown). Defeat all three in **any order** — server tracks an order-independent subset. + +## Expected encounter HUD progression + +| After | `EncounterProgressLabel` | `EncounterCompleteLabel` | +|-------|--------------------------|---------------------------| +| Boot (no engagement) | `prototype_combat_pocket: not started (0/3)` | `Loot: —` | +| 1st NPC defeated | `prototype_combat_pocket: 1/3` | `Loot: —` | +| 2nd NPC defeated | `prototype_combat_pocket: 2/3` | `Loot: —` | +| 3rd NPC defeated | `prototype_combat_pocket: completed (3/3)` | `scrap_metal_bulk ×10` and `contract_handoff_token ×1` (display names when item defs loaded) | +| After Godot restart (idempotency) | still **`completed (3/3)`** | unchanged grant lines | + +## Expected inventory after completion + +On a fresh server restart the bag starts empty. After the third defeat, **`InventoryLabel`** should show at least: + +| Item id | Quantity | +|---------|----------| +| `scrap_metal_bulk` | **10** | +| `contract_handoff_token` | **1** | + +Record these quantities before idempotency steps. + +## Expected gig XP after three defeats + +**`GigXpLabel`** should show **`breach: L1 · 75 xp`** (NEO-44: **25** gig XP per defeat × **3**). No further gig XP increase during idempotency steps. + +## Checklist + +### Boot + encounter clear + +1. Start server: `cd server/NeonSprawl.Server && dotnet run`. +2. Run Godot main scene (**F5**). Confirm boot lines: **`Encounter: … not started (0/3)`** and **`Loot: —`** on **`EncounterProgressLabel`** / **`EncounterCompleteLabel`** (combat HUD block, below **`PlayerCombatHpLabel`**). +3. Defeat **first** prototype NPC (any archetype): walk to marker, **Tab** lock, press **1** until **`Cast: … — defeated!`**. Verify **`EncounterProgressLabel`** shows **`1/3`** (may lag one GET behind cast — wait ~1 s). +4. Defeat **second** prototype NPC (either remaining archetype): lock, cast until defeated. Verify **`2/3`**. +5. Defeat **third** prototype NPC: lock, cast until defeated (**8** pulses for Elite). Verify **`completed (3/3)`** and **`EncounterCompleteLabel`** lists **`scrap_metal_bulk ×10`** and **`contract_handoff_token ×1`** (or raw ids if defs still loading). +6. Open **`Economy HUD`** body: **`InventoryLabel`** shows **`scrap_metal_bulk`** (**10**) and **`contract_handoff_token`** (**1**) without pressing **I** (auto-refresh on completion GET). +7. Verify **`GigXpLabel`** shows **`breach: L1 · 75 xp`**. + +### Idempotency + +8. **Record** inventory quantities for **`scrap_metal_bulk`** and **`contract_handoff_token`** from step 6. +9. **Godot restart (re-login):** stop Godot run (**Shift+F5** or stop button); **F5** again **without** stopping the server. After boot hydrate, verify **`EncounterProgressLabel`** still **`completed (3/3)`** and **`EncounterCompleteLabel`** grant lines unchanged. +10. Verify **`InventoryLabel`** quantities **match step 8** — no duplicate loot. +11. Lock a **defeated** NPC (**Tab** at its marker); press **1**. Verify **`CastFeedbackLabel`** shows **`ability_cast_denied: target_defeated`**; encounter HUD and inventory **unchanged** from steps 9–10. +12. Verify **`GigXpLabel`** still **`breach: L1 · 75 xp`** — no extra gig XP from idempotency casts. + +### Regression + +13. **`CastFeedbackLabel`** still shows readable deny reasons (NEO-28 / NEO-98). +14. Component encounter HUD detail: [NEO-110 manual QA](NEO-110.md). Component NPC combat: [NEO-98 manual QA](NEO-98.md). + +## Notes + +- Defeat order may differ from the archetype table — only **0/3 → 3/3** progression is asserted. +- Capstone baseline prefers **server restart** over mid-session fixture POST. +- **`POST /game/__dev/combat-targets-fixture`** is **out of scope** for idempotency — it clears encounter progress/completion alongside NPC HP reset ([server README — dev combat-target fixture](../../server/README.md#dev-combat-target-fixture-brunomanual-qa)). +- Slice 3 Epic AC: completing **`prototype_combat_pocket`** grants expected items + quest token **once** ([epic_05 Slice 3](../../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-3)). + +## Acceptance + +- [ ] Steps 1–14 completable in one session without Bruno/curl. +- [ ] Progress label shows **1/3**, **2/3**, then **completed (3/3)** after three defeats (any order). +- [ ] Loot label and inventory show **`scrap_metal_bulk` ×10** + **`contract_handoff_token` ×1**; quantities unchanged after Godot restart + defeated-NPC casts. +- [ ] Epic 5 Slice 3 AC satisfied in Godot.