Merge pull request #162 from ViPro-Technologies/NEO-123-playable-onboarding-quest-chain-capstone

NEO-123: Playable onboarding quest chain capstone (Godot)
pull/163/head
VinPropane 2026-06-07 15:09:03 -04:00 committed by GitHub
commit 31609917a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 371 additions and 13 deletions

View File

@ -239,6 +239,32 @@ Full checklist: [`docs/manual-qa/NEO-110.md`](../docs/manual-qa/NEO-110.md).
Full checklist: [`docs/manual-qa/NEO-122.md`](../docs/manual-qa/NEO-122.md).
## End-to-end onboarding quest loop (NEO-123)
Epic 7 Slice 1 capstone — complete all four prototype quests (three onboarding intros + operator chain) **in Godot** without Bruno.
**Flow:** fresh server restart → **Q** gather intro → **Shift+Q** combat intro → **Shift+Q** refine intro → **Shift+Q** operator chain → four chain steps → Godot restart idempotency.
| Step | Input / trigger | HUD / server outcome |
|------|-----------------|----------------------|
| Boot | Godot **F5** + fresh server | Four quests **`not started`**; empty bag |
| Gather intro | **Q** + **R** at nodes (×3 scrap) | Salvage Run **`active`** → **`completed`** |
| Combat intro | **Shift+Q** + defeat ×3 NPCs | Clear the Pocket **`completed`**; encounter **`completed (3/3)`**; **`contract_handoff_token`** in bag |
| Refine intro | **Shift+Q** + craft **`refine_scrap_standard`** | Refine Stock **`completed`** |
| Operator chain | **Shift+Q** + chain objectives | Operator Chain **`active step 1…4`** → **`completed`** when token held |
| Idempotency | Stop Godot + **F5** (server still running) | All four quests still **`completed`** |
| Duplicate accept | **Q** / **Shift+Q** on finished quests | **`already_completed`** / **`no eligible quest`**; HUD unchanged |
**Accept order note:** **Shift+Q** uses quest-definitions **catalog order** (ordinal `id`). After gather intro completes, combat intro is offered before refine intro — see [`docs/manual-qa/NEO-123.md`](../docs/manual-qa/NEO-123.md).
**Cross-links:** [NEO-112](../docs/plans/NEO-112-implementation-plan.md) catalog · [NEO-118](../docs/plans/NEO-118-implementation-plan.md) objective wiring · [NEO-119](../docs/plans/NEO-119-implementation-plan.md) quest-progress GET · [NEO-120](../docs/plans/NEO-120-implementation-plan.md) accept POST · [NEO-122](../docs/manual-qa/NEO-122.md) HUD components · [NEO-75](../docs/manual-qa/NEO-75.md) economy craft · [NEO-111](../docs/manual-qa/NEO-111.md) combat token loot.
**Scripts:** `quest_progress_client.gd`, `quest_definitions_client.gd`, `quest_hud_controller.gd`, `encounter_progress_client.gd`, `inventory_client.gd`, `craft_client.gd`, `ability_cast_client.gd` — wired from `main.gd`.
**Preconditions:** **Server restart** before capstone run resets quest progress, inventory, encounter state, and resource nodes.
Full capstone checklist: [`docs/manual-qa/NEO-123.md`](../docs/manual-qa/NEO-123.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.

View File

@ -7,7 +7,7 @@
| **Module ID** | E7.M1 |
| **Epic** | [Epic 7 — Quest / Faction](../epics/epic_07_quest_faction.md) |
| **Stage target** | Prototype |
| **Status** | Planned — Slice 1 backlog [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md): **E7M1-01** [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) catalog **landed** (schemas + CI); **E7M1-02** [NEO-113](https://linear.app/neon-sprawl/issue/NEO-113) server load **landed**; **E7M1-03** [NEO-114](https://linear.app/neon-sprawl/issue/NEO-114) registry **landed**; **E7M1-04** [NEO-115](https://linear.app/neon-sprawl/issue/NEO-115) HTTP read **landed**; **E7M1-05** [NEO-116](https://linear.app/neon-sprawl/issue/NEO-116) player quest state store **landed**; **E7M1-06** [NEO-117](https://linear.app/neon-sprawl/issue/NEO-117) **`QuestStateOperations`** **landed**; **E7M1-07** [NEO-118](https://linear.app/neon-sprawl/issue/NEO-118) **`QuestObjectiveWiring`** **landed**; **E7M1-08** [NEO-119](https://linear.app/neon-sprawl/issue/NEO-119) per-player GET **landed**; **E7M1-09** [NEO-120](https://linear.app/neon-sprawl/issue/NEO-120) accept POST **landed**; **E7M1-10** [NEO-121](https://linear.app/neon-sprawl/issue/NEO-121) telemetry hooks **landed**; **E7M1-11** [NEO-122](https://linear.app/neon-sprawl/issue/NEO-122) client quest HUD **landed**; capstone **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123) |
| **Status** | **Ready** — Slice 1 backlog [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md) **E7M1-01** [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123) **landed**. Client capstone [`NEO-123` manual QA](../../manual-qa/NEO-123.md); [client README — End-to-end onboarding quest loop (NEO-123)](../../../client/README.md#end-to-end-onboarding-quest-loop-neo-123). Epic 7 Slice 1 client capstone complete. |
| **Linear** | Label **`E7.M1`** · [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md) |
## Purpose

File diff suppressed because one or more lines are too long

View File

@ -107,9 +107,9 @@ Gameplay **content and curves** default to **boot load** with optional **dev-onl
| Module ID | Module Name | Depends On | Contract Needed | Phase Required | Status |
|---|---|---|---|---|---|
| E7.M1 | QuestStateMachine | E3.M2, E5.M1 | QuestDef, QuestStepState, QuestStateTransition | Prototype | Planned |
| E7.M1 | QuestStateMachine | E3.M2, E5.M1 | QuestDef, QuestStepState, QuestStateTransition | Prototype | Ready |
**E7.M1 note:** Epic 7 **Slice 1** backlog in Linear ([Epic 7 — Questing, Narrative, and Faction Reputation](https://linear.app/neon-sprawl/project/epic-7-questing-narrative-and-faction-reputation-a9416783ceee)): [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) → [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123); label **`E7.M1`**. See [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md), [E7_M1_QuestStateMachine.md](E7_M1_QuestStateMachine.md). Upstream: E3.M1 gather **Ready**, E3.M2 craft **Ready**, E5.M3 encounter **Ready**. Client capstone **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123). **E7M1-01 / NEO-112** catalog landed (schemas + CI); **E7M1-02 / NEO-113** server load landed; **E7M1-03 / NEO-114** registry landed ([NEO-114 plan](../../plans/NEO-114-implementation-plan.md)); **E7M1-04 / NEO-115** HTTP read landed ([NEO-115 plan](../../plans/NEO-115-implementation-plan.md)); **E7M1-05 / NEO-116** player quest state store landed ([NEO-116 plan](../../plans/NEO-116-implementation-plan.md)); **E7M1-06 / NEO-117** **`QuestStateOperations`** landed ([NEO-117 plan](../../plans/NEO-117-implementation-plan.md)); **E7M1-07 / NEO-118** **`QuestObjectiveWiring`** landed ([NEO-118 plan](../../plans/NEO-118-implementation-plan.md)); **E7M1-08 / NEO-119** per-player GET landed ([NEO-119 plan](../../plans/NEO-119-implementation-plan.md)); **E7M1-09 / NEO-120** accept POST landed ([NEO-120 plan](../../plans/NEO-120-implementation-plan.md)); **E7M1-10 / NEO-121** comment-only quest telemetry hook sites landed ([NEO-121 plan](../../plans/NEO-121-implementation-plan.md)); register row stays **Planned** until client capstone **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123).
**E7.M1 note:** Epic 7 **Slice 1** backlog in Linear ([Epic 7 — Questing, Narrative, and Faction Reputation](https://linear.app/neon-sprawl/project/epic-7-questing-narrative-and-faction-reputation-a9416783ceee)): [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) → [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123) **landed**; label **`E7.M1`**. See [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md), [E7_M1_QuestStateMachine.md](E7_M1_QuestStateMachine.md). Upstream: E3.M1 gather **Ready**, E3.M2 craft **Ready**, E5.M3 encounter **Ready**. **E7M1-12 / NEO-123** client capstone landed — [`NEO-123` manual QA](../../manual-qa/NEO-123.md), [client README — End-to-end onboarding quest loop (NEO-123)](../../../client/README.md#end-to-end-onboarding-quest-loop-neo-123), plan [NEO-123](../../plans/NEO-123-implementation-plan.md). Epic 7 Slice 1 client capstone complete.
| E7.M2 | RewardAndUnlockRouter | E2.M2, E3.M3, E7.M1 | QuestRewardBundle, UnlockGrant, RewardDeliveryEvent | Prototype | Planned |
| E7.M3 | FactionReputationLedger | E7.M1 | FactionStanding, ReputationDelta, FactionGateRule | Pre-production | Planned |
| E7.M4 | ContractMissionGenerator | E4.M1, E5.M3, E7.M3 | ContractTemplate, ContractSeed, ContractOutcome | Pre-production | Planned |

View File

@ -22,7 +22,7 @@
| **Q** accept gather | **`Intro: Salvage Run: active step 1`** | **`Quest accept: Intro: Salvage Run accepted`** |
| Gather scrap (**R** on resource node ×3) | Gather objective counter increases (e.g. **`gather_intro_obj_scrap=3`**) | unchanged |
| Gather quest completes | **`Intro: Salvage Run: completed`** | unchanged |
| **Shift+Q** accept refine (after gather complete) | **`Intro: Refine Stock: active step 1`** | **`Quest accept: Intro: Refine Stock accepted`** |
| **Shift+Q** after gather complete (catalog order) | **`Intro: Clear the Pocket: active`** — combat sorts before refine | **`Quest accept: Intro: Clear the Pocket accepted`** |
| Craft **`refine_scrap_standard`** (Economy HUD) | Refine quest completes when recipe succeeds | unchanged |
| Duplicate **Q** on gather | Gather row stays **`completed`** | **`Quest accept: denied — already_completed`** (or **`already_active`**) |
@ -33,9 +33,9 @@
3. Press **Q**. Verify gather intro becomes **`active step 1`** and accept feedback shows success.
4. Gather scrap until gather quest completes (resource node **R**). Verify counter updates on **`QuestProgressLabel`** after each successful gather (may lag one GET).
5. Press **Shift+Q** before gather is complete — verify **`Quest accept: denied — prerequisite_incomplete`** for refine (or **`no eligible quest`** if refine is not yet eligible).
6. After gather completes, press **Shift+Q**. Verify refine quest becomes **`active`**.
7. Open **Economy HUD** recipe panel — **Refine Scrap (Standard)** is the **first** row (scroll starts at top). Craft when **≥5** scrap in bag.
8. Craft **`refine_scrap_standard`** with Economy HUD open. Verify refine quest **`completed`** after successful craft.
6. After gather completes, press **Shift+Q**. Verify **combat** intro becomes **`active`** (catalog order — combat before refine). Component refine/craft path: complete combat first, then repeat **Shift+Q** for refine; see [NEO-123 capstone](NEO-123.md) for full four-quest order.
7. Open **Economy HUD** recipe panel — **Refine Scrap (Standard)** is the **first** row (scroll starts at top). After combat intro is **`completed`** and refine intro is **`active`**, craft when **≥5** scrap in bag.
8. Craft **`refine_scrap_standard`** with Economy HUD open. Verify refine quest **`completed`** after successful craft (requires refine intro **`active`** from step 6 follow-up **Shift+Q**).
9. Press **Q** again on already-completed gather. Verify deny feedback on **`QuestAcceptFeedbackLabel`** without Bruno.
10. Optional: stop server while Godot running, press **Q** — verify **`Quests: error — …`** or **`Quest accept: failed — …`** visible on HUD.
11. Optional: block **`GET /game/world/quest-definitions`** only while progress GET succeeds — verify **`definitions error — …`** under **`QuestProgressLabel`** with raw quest ids as fallback.
@ -43,8 +43,8 @@
## Notes
- Combat intro (**`prototype_quest_combat_intro`**) has no prerequisites — **Shift+Q** may offer it before refine if gather is still active; server deny is authoritative when prerequisites block accept.
- Full four-quest chain capstone: [NEO-123 manual QA](NEO-123.md).
- **Shift+Q** uses quest-definitions **catalog order** (ordinal `id`): after gather intro completes, **combat** intro is offered before **refine** intro. Server deny is authoritative when prerequisites block accept.
- Full four-quest chain capstone (supersedes partial intro path here): [NEO-123 manual QA](NEO-123.md).
- Encounter progress HUD regression: [NEO-110 manual QA](NEO-110.md).
## Acceptance

View File

@ -0,0 +1,143 @@
# NEO-123 — Manual QA checklist
| Field | Value |
|-------|-------|
| Key | NEO-123 |
| Title | E7M1-12: Playable onboarding quest chain capstone (Godot) |
| Linear | https://linear.app/neon-sprawl/issue/NEO-123/e7m1-12-playable-onboarding-quest-chain-capstone-godot |
| Plan | `docs/plans/NEO-123-implementation-plan.md` |
| Branch | `NEO-123-playable-onboarding-quest-chain-capstone` |
## Preconditions
- **Fresh dev player:** stop any running server, then start a new instance so in-memory **`dev-local-1`** quest progress, inventory, encounter progress, NPC HP, and resource-node depletion reset.
- **No Bruno/curl** for this checklist — accept uses **Q** / **Shift+Q**; gameplay uses **R**, **Tab** + **1**, and Economy HUD **Craft** buttons only.
- NEO-112NEO-122 quest spine and NEO-75/NEO-111 economy/combat capstones landed on `main`.
- **`Economy HUD`** toggle **on** for craft and inventory verification.
## Quest catalog (frozen)
| Quest id | Display name | Prerequisites | Objective |
|----------|--------------|---------------|-----------|
| `prototype_quest_gather_intro` | Intro: Salvage Run | none | gather **3** `scrap_metal_bulk` |
| `prototype_quest_combat_intro` | Intro: Clear the Pocket | none | `encounter_complete` **`prototype_combat_pocket`** |
| `prototype_quest_refine_intro` | Intro: Refine Stock | gather intro | craft **`refine_scrap_standard`** ×1 |
| `prototype_quest_operator_chain` | Operator Chain | all three intros | gather ×5 → refine ×1 → **`make_field_stim_mk0`** ×1 → hold **`contract_handoff_token`** ×1 |
## Accept key contract
| Key | Action |
|-----|--------|
| **Q** | Accept **`prototype_quest_gather_intro`** |
| **Shift+Q** | First catalog-order **`not_started`** quest with all **`prerequisiteQuestIds`** **`completed`** |
**Catalog order (ordinal `id`):** combat → gather → operator chain → refine. After gather intro completes, **Shift+Q** accepts **combat** (not refine). After combat completes, **Shift+Q** accepts **refine**. After all three intros complete, **Shift+Q** accepts **operator chain**.
## Resource node anchors (gather)
| Node id | Anchor | Yield per **R** |
|---------|--------|-----------------|
| `prototype_resource_node_alpha` | **(12, 6)** | +1 scrap |
| `prototype_subsurface_vein_beta` | **(18, 6)** | +2 scrap |
| `prototype_bio_mat_gamma` | **(12, 0)** | +3 scrap |
| `prototype_urban_bulk_delta` | **(18, 0)** | +5 scrap |
Nodes allow multiple gathers (`maxGathers` **10** each). Quest gather counters credit **scrap quantity granted**, not gather event count.
## Combat archetypes (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).
## Expected quest HUD progression
| Phase | `QuestProgressLabel` (high level) | `QuestAcceptFeedbackLabel` |
|-------|-----------------------------------|----------------------------|
| Boot | All four **`not started`** | **`Quest accept: — (Q gather / Shift+Q next)`** |
| Gather intro | Salvage Run **`active step 1`** → **`completed`** | **`accepted`** on **Q** |
| Combat intro | Clear the Pocket **`active`** → **`completed`** | **`accepted`** on **Shift+Q** |
| Refine intro | Refine Stock **`active`** → **`completed`** | **`accepted`** on **Shift+Q** |
| Operator chain | Operator Chain **`active step 1…4`** → **`completed`** | **`accepted`** on **Shift+Q** |
| Idempotency | All four still **`completed`** | **Q** deny / **Shift+Q** no eligible |
## Material math (suggested path)
| Phase | Actions | Bag after (approx.) |
|-------|---------|---------------------|
| Gather intro | **Q**; **R** at **gamma** once (+3 scrap) | 3 scrap |
| Combat intro | **Shift+Q**; defeat ×3 NPCs | 13 scrap, 1 token (+10 scrap loot) |
| Refine intro | **Shift+Q**; craft **`refine_scrap_standard`** | 8 scrap, 1 plate, 1 token |
| Chain step 1 | **Shift+Q**; **R** at **delta** once (+5 scrap) | 13 scrap; counter **5/5** |
| Chain step 2 | Craft **`refine_scrap_standard`** | 8 scrap, 2 plates, 1 token |
| Chain step 3 | Craft **`make_field_stim_mk0`** | 7 scrap, 0 plates, 1 stim, 1 token |
| Chain step 4 | Token already held from combat loot | Operator Chain **`completed`** |
Adjust gathers/crafts if bag counts differ; counters and craft denies are authoritative.
## Checklist
### Boot
1. Start server: `cd server/NeonSprawl.Server && dotnet run`.
2. Run Godot main scene (**F5**). Confirm **`QuestProgressLabel`** lists four **`not started`** quests and **`QuestAcceptFeedbackLabel`** shows the idle hint (in **`HudRootScroll`**). **`Economy HUD`** on; economy panel pinned bottom-left.
### Intro: Salvage Run (gather)
3. Press **Q**. Verify **`Intro: Salvage Run: active step 1`** and accept success feedback.
4. Walk to **gamma** **(12, 0)**; press **R** once. Verify counter **`chain_obj_gather`** is **not** present yet; gather intro counter increases (e.g. **`gather_intro_obj_scrap=3`**).
5. Verify **`Intro: Salvage Run: completed`**.
### Intro: Clear the Pocket (combat)
6. Press **Shift+Q**. Verify **`Intro: Clear the Pocket: active`** (combat intro — catalog order picks combat before refine).
7. Defeat all three prototype NPCs (**Tab** + **1**, any order). Verify **`EncounterProgressLabel`** **`completed (3/3)`** and **`EncounterCompleteLabel`** lists **`scrap_metal_bulk ×10`** + **`contract_handoff_token ×1`**.
8. Verify **`Intro: Clear the Pocket: completed`** and **`InventoryLabel`** shows **`contract_handoff_token`** (**1**).
### Intro: Refine Stock (craft)
9. Press **Shift+Q**. Verify **`Intro: Refine Stock: active step 1`**.
10. In Economy HUD, **Craft** **`refine_scrap_standard`** when **≥5** scrap in bag. Verify **`Intro: Refine Stock: completed`**.
### Operator Chain (four steps)
11. Press **Shift+Q**. Verify **`Operator Chain: active step 1`**.
12. **Chain step 1 — gather ×5:** press **R** at resource nodes until step advances (suggested: **delta** once for +5 scrap). Verify **`active step 2`** on **`QuestProgressLabel`**.
13. **Chain step 2 — refine:** craft **`refine_scrap_standard`**. Verify **`active step 3`**.
14. **Chain step 3 — stim:** craft **`make_field_stim_mk0`** (needs **2** plates + **1** scrap). Verify **`active step 4`**.
15. **Chain step 4 — token:** verify **`Operator Chain: completed`** without extra actions (token from step 8).
### Capstone snapshot
16. **Record** final **`QuestProgressLabel`**: all four quests **`completed`**.
### Idempotency (no duplicate quest completion)
17. **Godot restart:** stop Godot (**Shift+F5**); **F5** again **without** stopping the server. Verify all four quests still **`completed`**.
18. Press **Q** on gather intro. Verify **`Quest accept: denied — already_completed`** (or **`already_active`**).
19. Press **Shift+Q**. Verify **`Quest accept: no eligible quest`** (or equivalent deny). Quest HUD **unchanged** from step 16.
### Regression
20. Component quest HUD: [NEO-122 manual QA](NEO-122.md).
21. Encounter loot + token: [NEO-111 manual QA](NEO-111.md).
22. Economy craft panel: [NEO-75 manual QA](NEO-75.md).
## Notes
- **Shift+Q catalog order** is intentional for Slice 1 — combat intro before refine intro after gather completes. Full capstone supersedes [NEO-122](NEO-122.md) partial intro path.
- Combat defeat order is flexible; only **0/3 → completed (3/3)** is asserted.
- Chain terminal step is **`inventory_has_item`** — no token consume in Slice 1.
- **`POST /game/__dev/combat-targets-fixture`** is **out of scope** — clears encounter progress unsuitable for idempotency ([server README — dev combat-target fixture](../../server/README.md#dev-combat-target-fixture-brunomanual-qa)).
- Epic 7 Slice 1 AC: 3 onboarding + 1 chain complete without duped completion ([epic_07 Definition of Done](../decomposition/epics/epic_07_quest_faction.md#epic-7-definition-of-done)).
## Acceptance
- [x] Steps 119 completable in one session without Bruno/curl.
- [x] All four prototype quests reach **`completed`**; operator chain finishes when **`contract_handoff_token`** held.
- [x] Godot restart + duplicate **Q**/**Shift+Q** leave quest statuses unchanged (no duplicate completion).
- [x] Epic 7 Slice 1 AC satisfied in Godot.

View File

@ -368,9 +368,11 @@ Working backlog for **Epic 7 — Slice 1** ([quest core and persistence](../deco
**Acceptance criteria**
- [ ] Human completes **`docs/manual-qa/NEO-123.md`** with server + client.
- [ ] Epic 7 Slice 1 AC: 3 onboarding + 1 chain complete without duplicate completion on replay.
- [ ] Re-read [epic_07 Definition of Done](../decomposition/epics/epic_07_quest_faction.md#definition-of-done) for prototype minimums.
- [x] Human completes **`docs/manual-qa/NEO-123.md`** with server + client.
- [x] Epic 7 Slice 1 AC: 3 onboarding + 1 chain complete without duplicate completion on replay.
- [x] Re-read [epic_07 Definition of Done](../decomposition/epics/epic_07_quest_faction.md#definition-of-done) for prototype minimums.
**Landed (NEO-123):** [`docs/manual-qa/NEO-123.md`](../manual-qa/NEO-123.md); [client README — End-to-end onboarding quest loop (NEO-123)](../../client/README.md#end-to-end-onboarding-quest-loop-neo-123); plan [NEO-123](../plans/NEO-123-implementation-plan.md); E7.M1 **Ready**; Epic 7 Slice 1 client capstone complete.
**Client counterpart:** this story **is** the Slice 1 client capstone.

View File

@ -0,0 +1,187 @@
# NEO-123 — Implementation plan
## Story reference
| Field | Value |
|--------|--------|
| **Key** | NEO-123 |
| **Title** | E7M1-12: Playable onboarding quest chain capstone (Godot) |
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-123/e7m1-12-playable-onboarding-quest-chain-capstone-godot |
| **Module** | [E7.M1 — QuestStateMachine](../decomposition/modules/E7_M1_QuestStateMachine.md) · Epic 7 Slice 1 · backlog **E7M1-12** |
| **Branch** | `NEO-123-playable-onboarding-quest-chain-capstone` |
| **Client deps** | [NEO-122](https://linear.app/neon-sprawl/issue/NEO-122) quest progress + accept HUD (**Done**); [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) encounter clear capstone; [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) economy loop; [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) encounter HUD |
| **Server deps** | [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112)[NEO-121](https://linear.app/neon-sprawl/issue/NEO-121) quest spine (**landed on `main`**) |
| **Pattern** | Capstone integration — [NEO-111](NEO-111-implementation-plan.md) docs + manual QA primary; minimal gap-fill only if capstone QA fails on `main` |
| **Blocked by** | [NEO-122](https://linear.app/neon-sprawl/issue/NEO-122) (**Done**) |
| **Server counterpart** | NEO-112NEO-121 server spine; Bruno-only verification is not prototype-complete per [full-stack epic decomposition](../../.cursor/rules/full-stack-epic-decomposition.md) |
## Kickoff clarifications
| Topic | Question | Agent recommendation | Answer |
|--------|----------|----------------------|--------|
| **Implementation scope** | Docs-only vs client code? | **Docs + manual QA primary**; fix integration gaps only if capstone QA fails on `main` — [NEO-111](NEO-111-implementation-plan.md) precedent. | **Adopted** — docs-primary |
| **Capstone intro accept order** | Scripted order for three onboarding intros? | **Shift+Q catalog order:** **Q** gather → **Shift+Q** combat → **Shift+Q** refine → **Shift+Q** operator chain — `prototype_quest_combat_intro` sorts before `prototype_quest_refine_intro` in defs order; matches `quest_hud_controller.gd` `_find_first_eligible_quest_id()`. | **Adopted** — Shift+Q catalog order |
| **Idempotency verification** | How to prove no duplicate quest completion on replay? | **Godot stop + F5 restart** (server still running) + retry **Q** / **Shift+Q** on completed quests; HUD statuses unchanged — mirrors [NEO-111](../manual-qa/NEO-111.md). | **Adopted** — Godot restart + duplicate accepts |
| **NEO-122 relationship** | Supersede component QA? | **NEO-123 supersedes as Slice 1 capstone**; keep [NEO-122](../manual-qa/NEO-122.md) as component-level regression (NEO-111 / NEO-110 pattern). | **Adopted** (default; no kickoff question) |
**Additional defaults (no kickoff question — settled by backlog / precedent):**
- **Session baseline:** fresh **server restart** before Godot **F5** — resets in-memory quest progress, inventory, encounter state, and resource-node depletion; zero Bruno/curl in main checklist.
- **Economy HUD:** toggle **on** for craft/inventory verification (NEO-75 / NEO-122 precedent).
- **Server changes:** none unless capstone QA exposes an authoritative bug (unlikely — covered by server quest tests).
## Goal, scope, and out-of-scope
**Goal:** Prove Epic 7 Slice 1 acceptance **in Godot**: complete all four prototype quests (three onboarding intros + one four-step operator chain) without Bruno; satisfies vision **quest loop** gate and [epic_07 Definition of Done](../decomposition/epics/epic_07_quest_faction.md#epic-7-definition-of-done).
**In scope (from Linear + [E7M1-12](E7M1-prototype-backlog.md#e7m1-12--playable-onboarding-quest-chain-capstone-godot)):**
- **`docs/manual-qa/NEO-123.md`**: numbered **single-session** capstone — fresh server restart; accept intros in **Shift+Q catalog order**; perform gather/craft/combat actions; verify **`prototype_quest_operator_chain`** completes when **`contract_handoff_token`** held; **idempotency** via Godot restart + duplicate accept keys.
- **`client/README.md`**: **End-to-end onboarding quest loop** section — integration flow table; cross-links NEO-112NEO-122 and economy/combat capstones.
- **Module alignment** (on story completion): update [E7_M1](../decomposition/modules/E7_M1_QuestStateMachine.md) status, [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) E7.M1 row, [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) E7.M1 note, and [E7M1-prototype-backlog.md](E7M1-prototype-backlog.md) E7M1-12 checkboxes; mark Epic 7 Slice 1 client capstone complete.
- **Integration fixes only if capstone QA fails** on current `main` wiring (HUD refresh gap, eligible-accept edge case, chain step stall, idempotency breach, etc.).
**Out of scope (from Linear + backlog):**
- E7.M2 reward bundles; faction reputation; quest VFX art.
- Bruno-only verification as prototype-complete proof.
- New server HTTP routes or Godot HUD features beyond NEO-122.
- Eligible-quest picker redesign (catalog order is the documented contract for Slice 1).
## Acceptance criteria checklist
- [x] Human completes **`docs/manual-qa/NEO-123.md`** with server + client.
- [x] Epic 7 Slice 1 AC: 3 onboarding + 1 chain complete without duplicate completion on replay (idempotency steps pass).
- [x] Re-read [epic_07 Definition of Done](../decomposition/epics/epic_07_quest_faction.md#epic-7-definition-of-done) for prototype minimums.
## Implementation reconciliation (shipped)
**Manual QA:** [`docs/manual-qa/NEO-123.md`](../manual-qa/NEO-123.md) — passed (four-quest Godot capstone, Shift+Q catalog accept order, idempotency steps).
- **`docs/manual-qa/NEO-123.md`** — single-session four-quest capstone with Shift+Q catalog accept order, material math, idempotency steps.
- **`client/README.md`** — **End-to-end onboarding quest loop (NEO-123)** section with flow table and cross-links.
- **`docs/manual-qa/NEO-122.md`** — catalog-order note + capstone cross-link; expected HUD table aligned with Shift+Q picker.
- **`docs/plans/E7M1-prototype-backlog.md`**, **`E7_M1_QuestStateMachine.md`**, **`documentation_and_implementation_alignment.md`**, **`module_dependency_register.md`** — E7M1-12 landed; E7.M1 **Ready**; Epic 7 Slice 1 client capstone complete.
- **No integration code changes** — docs-primary scope; server quest spine verified by existing quest API / wiring tests.
## Technical approach
### 1. Capstone manual QA script (`docs/manual-qa/NEO-123.md`)
**Preconditions:** Stop any running server; start fresh (`cd server/NeonSprawl.Server && dotnet run`) so in-memory **`dev-local-1`** quest progress, inventory, encounter progress, NPC HP, and resource-node depletion reset. Godot **F5** with **no** prior curl/Bruno seeding. **`Economy HUD`** toggle **on**.
**Quest catalog (frozen — `content/quests/prototype_quests.json`):**
| Quest id | Display name | Prerequisites | Steps |
|----------|--------------|---------------|-------|
| `prototype_quest_gather_intro` | Intro: Salvage Run | none | gather **3** `scrap_metal_bulk` |
| `prototype_quest_combat_intro` | Intro: Clear the Pocket | none | `encounter_complete` **`prototype_combat_pocket`** |
| `prototype_quest_refine_intro` | Intro: Refine Stock | gather intro | craft **`refine_scrap_standard`** ×1 |
| `prototype_quest_operator_chain` | Operator Chain | all three intros | gather ×5 → refine ×1 → **`make_field_stim_mk0`** ×1 → **`inventory_has_item`** **`contract_handoff_token`** ×1 |
**Accept key contract (NEO-122 / `quest_hud_controller.gd`):**
| Key | Action |
|-----|--------|
| **Q** | Accept **`prototype_quest_gather_intro`** |
| **Shift+Q** | First catalog-order **`not_started`** quest with all **`prerequisiteQuestIds`** **`completed`** |
**HTTP defs catalog order (ordinal `id`):** combat → gather → operator chain → refine. After gather intro completes, **Shift+Q** accepts **combat** (not refine). After combat completes, **Shift+Q** accepts **refine**. After all three intros complete, **Shift+Q** accepts **operator chain**.
**Expected quest HUD progression (`QuestProgressLabel`):**
| Phase | Action | Expected HUD lines (high level) |
|-------|--------|--------------------------------|
| Boot | Godot **F5** | All four quests **`not started`** |
| Gather intro | **Q** → gather **3** scrap (**R** at nodes) | Salvage Run **`active`** → counter → **`completed`** |
| Combat intro | **Shift+Q** → defeat ×3 NPCs (**Tab** + **1**) | Clear the Pocket **`active`** → **`completed`**; encounter **`completed (3/3)`**; loot includes **`contract_handoff_token`** |
| Refine intro | **Shift+Q** → craft **`refine_scrap_standard`** | Refine Stock **`active`** → **`completed`** |
| Operator chain | **Shift+Q** → chain steps | Operator Chain **`active step 1…4`** → **`completed`** when token held |
| Idempotency | Godot restart + **Q** / **Shift+Q** | All four remain **`completed`**; accept deny / no eligible quest |
**Material / action notes for script author:**
- Combat loot grants **`scrap_metal_bulk` ×10** + **`contract_handoff_token` ×1** (NEO-111) — satisfies chain terminal **`inventory_has_item`** once combat intro completes (token persists for chain step 4).
- Chain step 1 requires **5** successful gathers **while chain step 1 is active** (objective counter; prior bag scrap does not substitute). Use [NEO-75](../manual-qa/NEO-75.md) anchor table (**alpha** +1, **beta** +2, **gamma** +3, **delta** +5).
- Chain step 3 **`make_field_stim_mk0`** needs **2** `refined_plate_stock` + **1** scrap — plan extra **`refine_scrap_standard`** crafts after combat loot if bag is short on plates.
- Defeat order for combat pocket is **flexible** (NEO-111); script asserts **0/3 → completed (3/3)** only.
**Main checklist spine (to expand in `docs/manual-qa/NEO-123.md`):**
1. Start server + Godot **F5**; confirm four quests **`not started`**.
2. **Q** accept gather intro; gather until **`Intro: Salvage Run: completed`**.
3. **Shift+Q** accept combat intro; defeat three prototype NPCs; confirm encounter + combat intro **`completed`** and bag contains **`contract_handoff_token`**.
4. **Shift+Q** accept refine intro; craft **`refine_scrap_standard`**; confirm refine intro **`completed`**.
5. **Shift+Q** accept operator chain; complete steps 14 (gather ×5, refine, stim craft, token check); confirm **`Operator Chain: completed`**.
6. **Record** final quest HUD snapshot (all four **`completed`**).
7. **Idempotency — Godot restart:** stop Godot; **F5** again **without** stopping server. Quest HUD still shows all four **`completed`**.
8. **Idempotency — duplicate accepts:** **Q** on gather → **`denied — already_completed`**; **Shift+Q****`no eligible quest`** (or deny on ineligible pick); HUD unchanged from step 6.
9. Regression pointers: [NEO-122](../manual-qa/NEO-122.md) component HUD, [NEO-111](../manual-qa/NEO-111.md) encounter loot, [NEO-75](../manual-qa/NEO-75.md) economy craft.
### 2. Client README (`client/README.md`)
Add **End-to-end onboarding quest loop (NEO-123)** section after [Quest progress + accept HUD (NEO-122)](../../client/README.md#quest-progress--accept-hud-neo-122), mirroring NEO-111 / NEO-75 capstone sections:
- One-line Epic 7 Slice 1 capstone goal.
- Flow table: fresh restart → **Q** gather → **Shift+Q** combat → **Shift+Q** refine → **Shift+Q** chain → four-step chain objectives → idempotency.
- Cross-links: NEO-112 catalog, NEO-118 wiring, NEO-119/120 HTTP, NEO-122 HUD, NEO-75 economy, NEO-111 combat token.
- Pointer to **`docs/manual-qa/NEO-123.md`**.
### 3. Module / backlog alignment (on land)
When capstone QA passes:
- [E7M1-prototype-backlog.md](E7M1-prototype-backlog.md) — E7M1-12 AC checkboxes.
- [E7_M1_QuestStateMachine.md](../decomposition/modules/E7_M1_QuestStateMachine.md) — module status **Ready**; Slice 1 client capstone note.
- [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) — E7.M1 row: NEO-123 landed; Epic 7 Slice 1 client capstone complete.
- [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) — E7.M1 **Ready** when all stories landed.
### 4. Integration fixes (conditional)
Run capstone QA on `main` (or branch with NEO-122 merged). If any step fails:
| Symptom | Likely fix surface |
|---------|-------------------|
| Quest HUD stale after gather/craft/defeat | `main.gd` refresh hook or `quest_hud_controller.gd` |
| Chain step counter stuck | Server wiring (unlikely) — file issue; client displays GET only |
| Token step not completing | Verify encounter loot + quest GET refresh on encounter **`completed`** (NEO-110 belt-and-suspenders) |
| Shift+Q picks unexpected quest | Document vs fix picker — **out of scope** unless script order is impossible |
Document any fix in this plans **Implementation reconciliation** section and add regression note to manual QA.
## Files to add
| Path | Purpose |
|------|---------|
| `docs/plans/NEO-123-implementation-plan.md` | This plan. |
| `docs/manual-qa/NEO-123.md` | Single-session four-quest capstone checklist with idempotency + regression steps. |
## Files to modify
| Path | Rationale |
|------|-----------|
| `client/README.md` | **End-to-end onboarding quest loop (NEO-123)** section with flow table and cross-links. |
| `docs/manual-qa/NEO-122.md` | Cross-link to NEO-123 capstone; note intro accept order defers to capstone script. |
| `docs/plans/E7M1-prototype-backlog.md` | E7M1-12 AC checkboxes when capstone lands. |
| `docs/decomposition/modules/E7_M1_QuestStateMachine.md` | Module status **Ready** + capstone landed note on completion. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E7.M1 row: NEO-123 landed; Slice 1 complete. |
| `docs/decomposition/modules/module_dependency_register.md` | E7.M1 **Ready** when capstone lands. |
**Conditional (only if capstone QA fails):** `client/scripts/main.gd`, `client/scripts/quest_hud_controller.gd`, or related refresh wiring — minimal gap-fill; document paths in reconciliation.
## Tests
| Test file | What it covers |
|-----------|----------------|
| *(none planned)* | **Docs-primary scope** — server quest spine covered by **`QuestProgressApiTests`**, **`QuestAcceptApiTests`**, **`QuestObjectiveWiringTests`**, and content CI; client covered by NEO-122 GdUnit suites. Capstone proof is **`docs/manual-qa/NEO-123.md`** human QA. |
If integration fixes land, add or extend GdUnit only when the fix introduces new client parse/refresh behavior worth locking (follow [testing-expectations.md](../../.cursor/rules/testing-expectations.md) AAA layout).
## Open questions / risks
| Item | Agent recommendation | Status |
|------|----------------------|--------|
| **NEO-122 manual QA step 6 (Shift+Q → refine)** | Capstone adopts **catalog order** (combat before refine); update NEO-122 cross-link note, not NEO-122 checklist retroactively unless user asks. | **adopted** |
| **Resource node depletion mid-session** | Script uses four anchors from NEO-75; fresh server gives enough total yield for intro + chain gathers. | **adopted** |
| **Chain step 4 before combat loot** | Script orders combat intro before chain accept; token in bag before chain step 4. | **adopted** |
| **Docs-only fails capstone** | Escalate to minimal client refresh fix in same story; record in reconciliation. | **deferred** |