From 63156c353fb7d7bdf163134fd6fd38db2297401f Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 14 Jun 2026 14:08:02 -0400 Subject: [PATCH] NEO-132: Ship Slice 2 quest reward capstone docs and economy refresh. Add NEO-132 manual QA extending NEO-123 with reward HUD and economy verification, README capstone section, E7.M2 Ready alignment, client completion-transition signal for inventory/skill refresh, and GdUnit coverage for the new signal. --- client/README.md | 30 ++++- client/scripts/main.gd | 14 +++ client/scripts/quest_hud_controller.gd | 5 + client/test/quest_reward_hud_test.gd | 26 ++++ .../modules/E7_M2_RewardAndUnlockRouter.md | 4 +- ...umentation_and_implementation_alignment.md | 2 +- .../modules/module_dependency_register.md | 4 +- docs/manual-qa/NEO-123.md | 1 + docs/manual-qa/NEO-131.md | 2 + docs/manual-qa/NEO-132.md | 118 ++++++++++++++++++ docs/plans/E7M2-prototype-backlog.md | 2 + docs/plans/NEO-132-implementation-plan.md | 18 ++- 12 files changed, 216 insertions(+), 10 deletions(-) create mode 100644 docs/manual-qa/NEO-132.md diff --git a/client/README.md b/client/README.md index 0fca811..32f161b 100644 --- a/client/README.md +++ b/client/README.md @@ -246,11 +246,39 @@ Full checklist: [`docs/manual-qa/NEO-122.md`](../docs/manual-qa/NEO-122.md). - **HUD:** - **`UICanvas/HudRootScroll/HudRoot/QuestRewardDeliveryLabel`** — **`Quest rewards: —`** until a quest newly becomes **`completed`** in-session; then quest display name + grant lines. - Item lines use **`ItemDefinitionsClient.display_name_for`** (NEO-110 loot precedent); skill XP lines use title-case **`skillId`** + amount (e.g. **`Salvage +25 XP`**). -- **Refresh:** reuses NEO-122 triggers (boot hydrate + gather/craft/defeat/accept). **Transition-only** — boot with already-completed quests does not replay grant copy (NEO-123 idempotency). +- **Refresh:** reuses NEO-122 triggers (boot hydrate + gather/craft/defeat/accept). **Transition-only** — boot with already-completed quests does not replay grant copy (NEO-123 idempotency). **Economy HUD** (inventory + skills) refreshes on completion transition via NEO-132 signal from `quest_hud_controller.gd`. - **Errors:** failed quest-progress GET shows **`sync error — …`** on **`QuestProgressLabel`**; reward label **`—`** (NEO-122 pattern). Full checklist: [`docs/manual-qa/NEO-131.md`](../docs/manual-qa/NEO-131.md). +## End-to-end quest reward loop (NEO-132) + +Epic 7 Slice 2 capstone — complete all four prototype quests **in Godot** and verify quest completion bundles (skill XP + item grants) apply **once** with idempotent replay. + +**Flow:** fresh server restart → NEO-123 four-quest path → per-completion **`QuestRewardDeliveryLabel`** + economy HUD verification → Godot restart idempotency. + +| Step | Input / trigger | HUD / server outcome | +|------|-----------------|----------------------| +| Boot | Godot **F5** + fresh server | Four quests **`not started`**; reward label **`—`**; skill xp **0** | +| Gather intro | **Q** + gather ×3 scrap | Salvage Run **`completed`**; **`Salvage +25 XP`** reward line; salvage xp **+25** | +| Combat intro | **Shift+Q** + defeat ×3 NPCs | Clear the Pocket **`completed`**; encounter loot on **`EncounterCompleteLabel`**; **`Salvage +25 XP`** reward line; salvage xp **+25** | +| Refine intro | **Shift+Q** + craft **`refine_scrap_standard`** | Refine Stock **`completed`**; **`Refine +25 XP`** reward line; refine xp **+25** | +| Operator chain | **Shift+Q** + chain objectives | Operator Chain **`completed`**; **`Survey Drone Kit ×1`** + **`Salvage +50 XP`**; bag contains kit | +| Idempotency | Stop Godot + **F5** (server still running) | Quests still **`completed`**; reward label **`—`**; economy counts unchanged | +| Duplicate accept | **Q** / **Shift+Q** on finished quests | Deny; no duplicate skill XP or items | + +**Reward vs encounter loot:** quest completion bundles (`completionRewardBundle`) surface on **`QuestRewardDeliveryLabel`**; encounter pocket loot remains on **`EncounterCompleteLabel`** (E7M2 freeze — encounter loot unchanged). + +**Economy refresh:** on in-session quest completion transition, **`InventoryLabel`** + **`SkillProgressionLabel`** auto-refresh (NEO-132); reward label paint remains transition-only (NEO-131). + +**Cross-links:** [NEO-124](../docs/plans/NEO-124-implementation-plan.md) catalog freeze · [NEO-126](../docs/plans/NEO-126-implementation-plan.md) delivery store · [NEO-127](../docs/plans/NEO-127-implementation-plan.md) router · [NEO-129](../docs/plans/NEO-129-implementation-plan.md) HTTP summary · [NEO-131](../docs/manual-qa/NEO-131.md) reward HUD · [NEO-123](../docs/manual-qa/NEO-123.md) onboarding flow. + +**Scripts:** `quest_hud_controller.gd` (completion transition signal → economy refresh), `quest_progress_client.gd`, `inventory_client.gd`, `skill_progression_client.gd` — wired from `main.gd`. + +**Preconditions:** **Server restart** before capstone run resets quest progress, reward delivery store, inventory, and skill progression. + +Full capstone checklist: [`docs/manual-qa/NEO-132.md`](../docs/manual-qa/NEO-132.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. diff --git a/client/scripts/main.gd b/client/scripts/main.gd index 0e17cd2..c470752 100644 --- a/client/scripts/main.gd +++ b/client/scripts/main.gd @@ -867,6 +867,20 @@ func _setup_quest_progress_sync() -> void: _quest_reward_delivery_label, _item_defs_client ) + if _quest_hud.has_signal("quest_completion_reward_transition"): + _quest_hud.connect( + "quest_completion_reward_transition", Callable(self, "_on_quest_completion_reward_transition") + ) + + +func _on_quest_completion_reward_transition(_quest_id: String, _summary: Dictionary) -> void: + # NEO-132: refresh economy HUD after server applies quest completion bundle. + _request_inventory_refresh() + if ( + is_instance_valid(_skill_progression_client) + and _skill_progression_client.has_method("request_sync_from_server") + ): + _skill_progression_client.call("request_sync_from_server") func _request_quest_progress_refresh() -> void: diff --git a/client/scripts/quest_hud_controller.gd b/client/scripts/quest_hud_controller.gd index 6fba53e..d92201c 100644 --- a/client/scripts/quest_hud_controller.gd +++ b/client/scripts/quest_hud_controller.gd @@ -2,6 +2,9 @@ extends Node ## NEO-122: quest progress + accept HUD wiring (extracted from main.gd). ## NEO-131: quest completion reward label on in-session completed transition. +## NEO-132: emits quest_completion_reward_transition for economy HUD refresh. + +signal quest_completion_reward_transition(quest_id: String, summary: Dictionary) const QuestProgressClient := preload("res://scripts/quest_progress_client.gd") const GATHER_QUEST_ID := "prototype_quest_gather_intro" @@ -205,6 +208,8 @@ func _detect_and_apply_completion_transitions(snapshot: Dictionary) -> void: _last_reward_summary = (summary_variant as Dictionary).duplicate(true) else: _last_reward_summary = {} + if not _last_reward_quest_id.is_empty() and not _last_reward_summary.is_empty(): + quest_completion_reward_transition.emit(_last_reward_quest_id, _last_reward_summary) func _quest_ids_in_render_order(snapshot: Dictionary) -> Array: diff --git a/client/test/quest_reward_hud_test.gd b/client/test/quest_reward_hud_test.gd index dd8bb14..c656151 100644 --- a/client/test/quest_reward_hud_test.gd +++ b/client/test/quest_reward_hud_test.gd @@ -1,6 +1,7 @@ extends GdUnitTestSuite ## NEO-131: quest completion reward label HUD tests. +## NEO-132: quest_completion_reward_transition signal for economy refresh. const QuestHudController := preload("res://scripts/quest_hud_controller.gd") const QuestDefinitionsClient := preload("res://scripts/quest_definitions_client.gd") @@ -272,3 +273,28 @@ func test_sync_failure_shows_progress_error_and_idle_reward_label() -> void: # Assert assert_str(progress_label.text).contains("error — HTTP 503") assert_str(reward_label.text).is_equal("Quest rewards:\n—") + + +func test_completion_transition_emits_economy_refresh_signal() -> void: + # Arrange + var controller: Node = QuestHudController.new() + auto_free(controller) + add_child(controller) + monitor_signals(controller) + # Act + controller.call("_on_progress_received", _active_gather_snapshot()) + controller.call("_on_progress_received", _completed_gather_with_summary_snapshot()) + # Assert + await assert_signal(controller).is_emitted("quest_completion_reward_transition", any()) + + +func test_boot_completed_does_not_emit_economy_refresh_signal() -> void: + # Arrange + var controller: Node = QuestHudController.new() + auto_free(controller) + add_child(controller) + monitor_signals(controller) + # Act + controller.call("_on_progress_received", _completed_gather_with_summary_snapshot()) + # Assert + await assert_signal(controller).wait_until(200).is_not_emitted("quest_completion_reward_transition") diff --git a/docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md b/docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md index ca5d52b..1988986 100644 --- a/docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md +++ b/docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md @@ -7,7 +7,7 @@ | **Module ID** | E7.M2 | | **Epic** | [Epic 7 — Quest / Faction](../epics/epic_07_quest_faction.md) | | **Stage target** | Prototype | -| **Status** | Planned — Slice 2 backlog [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md): **E7M2-01** [NEO-124](https://linear.app/neon-sprawl/issue/NEO-124) → **E7M2-09** [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132). **NEO-43 prep landed:** `MissionRewardSkillXpGrant` for bundle skill XP (`mission_reward`). Upstream: E7.M1 **Ready**, E2.M2 grant stack, E3.M3 inventory. | +| **Status** | Ready — Slice 2 backlog [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md): **E7M2-01** [NEO-124](https://linear.app/neon-sprawl/issue/NEO-124) → **E7M2-09** [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132) **landed**. **NEO-43 prep landed:** `MissionRewardSkillXpGrant` for bundle skill XP (`mission_reward`). Upstream: E7.M1 **Ready**, E2.M2 grant stack, E3.M3 inventory. | ## Purpose @@ -79,3 +79,5 @@ Epic 7 **Slice 2** — `reward_delivery`, `unlock_granted`; no double-claim on r **NEO-130 (E7M2-07 telemetry hooks):** comment-only **`reward_delivery`** + **`unlock_granted`** stub hook sites in **`RewardRouterOperations.TryDeliverQuestCompletion`** — first-time **`TryRecord`** success and future **`UnlockGrant`** apply region respectively; no production ingest. See [NEO-130 implementation plan](../../plans/NEO-130-implementation-plan.md); [server README — Reward telemetry hooks (NEO-130)](../../../server/README.md#reward-telemetry-hooks-neo-130). **NEO-131 (E7M2-08 client HUD):** Godot **`QuestRewardDeliveryLabel`** + **`quest_hud_controller.gd`** transition detection paints **`completionRewardSummary`** grant lines when a quest newly becomes **`completed`** in-session (parse via **`quest_progress_client.gd`**). See [NEO-131 implementation plan](../../plans/NEO-131-implementation-plan.md); [client README — Quest completion reward HUD (NEO-131)](../../../client/README.md#quest-completion-reward-hud-neo-131). + +**NEO-132 (E7M2-09 client capstone):** playable four-quest reward delivery loop — [`NEO-132` manual QA](../../manual-qa/NEO-132.md); [client README — End-to-end quest reward loop (NEO-132)](../../../client/README.md#end-to-end-quest-reward-loop-neo-132); economy HUD refresh on completion transition via **`quest_completion_reward_transition`** signal. Epic 7 Slice 2 client capstone complete. diff --git a/docs/decomposition/modules/documentation_and_implementation_alignment.md b/docs/decomposition/modules/documentation_and_implementation_alignment.md index c64b474..909193a 100644 --- a/docs/decomposition/modules/documentation_and_implementation_alignment.md +++ b/docs/decomposition/modules/documentation_and_implementation_alignment.md @@ -59,7 +59,7 @@ Rows appear when work starts; default for unlisted modules is **Planned** / not | E5.M1 | Ready | **NEO-76 landed:** frozen prototype four-ability catalog in [`content/abilities/prototype_abilities.json`](../../../content/abilities/prototype_abilities.json); [`ability-def.schema.json`](../../../content/schemas/ability-def.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) E5M1 four-id gate ([NEO-76](../../plans/NEO-76-implementation-plan.md)). **NEO-77 landed:** fail-fast server load of `content/abilities/*_abilities.json` at startup — `server/NeonSprawl.Server/Game/Combat/` ([NEO-77](../../plans/NEO-77-implementation-plan.md)); [server README — Ability catalog](../../../server/README.md#ability-catalog-contentabilities-neo-77). **NEO-79 landed:** injectable **`IAbilityDefinitionRegistry`** + DI; hotbar/cast use **`TryNormalizeKnown`** ([NEO-79](../../plans/NEO-79-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/hotbar-loadout/` + `ability-cast/` unknown-ability deny smokes. **NEO-78 landed:** **`GET /game/world/ability-definitions`** — `AbilityDefinitionsWorldApi` + DTOs in `Game/Combat/` ([NEO-78](../../plans/NEO-78-implementation-plan.md)); [server README — Ability definitions (NEO-78)](../../../server/README.md#ability-definitions-neo-78); Bruno `bruno/neon-sprawl-server/ability-definitions/`. **NEO-80 landed:** **`ICombatEntityHealthStore`** / **`InMemoryCombatEntityHealthStore`** — lazy init (Slice 1 flat 100 HP; **superseded by [NEO-91](../../plans/NEO-91-implementation-plan.md)** per-archetype catalog max HP + three NPC instance ids); DI via **`AddCombatEntityHealthStore()`** ([NEO-80](../../plans/NEO-80-implementation-plan.md)); [server README — Combat entity health (NEO-80, NEO-91)](../../../server/README.md#combat-entity-health-neo-80-neo-91). **NEO-81 landed:** **`CombatOperations.TryResolve`** + **`CombatResult`** + **`CombatReasonCodes`** — defeated pre-check deny, catalog damage ([NEO-81](../../plans/NEO-81-implementation-plan.md)); [server README — Combat engine (NEO-81)](../../../server/README.md#combat-engine-neo-81). **NEO-82 landed:** **`AbilityCastApi`** → **`CombatOperations.TryResolve`**; nested wire **`combatResolution`** on accept; per-ability catalog cooldown; **`target_defeated`** cast deny ([NEO-82](../../plans/NEO-82-implementation-plan.md)); [server README — Ability cast (NEO-82)](../../../server/README.md#ability-cast-neo-31-neo-82); Bruno `bruno/neon-sprawl-server/ability-cast/` defeat spine. **NEO-83 landed:** **`GET /game/world/combat-targets`** — `CombatTargetsWorldApi` + DTOs; authoritative HP snapshot from **`ICombatEntityHealthStore`** ([NEO-83](../../plans/NEO-83-implementation-plan.md)); [server README — Combat targets snapshot (NEO-83)](../../../server/README.md#combat-targets-snapshot-neo-83); Bruno `bruno/neon-sprawl-server/combat-targets/`. **NEO-44 landed:** **`CombatDefeatGigXpGrant`** on cast **`targetDefeated`** — **25** gig XP to **`breach`** via **`IPlayerGigProgressionStore`** (V007); **`GET …/gig-progression`** ([NEO-44](../../plans/NEO-44-implementation-plan.md), [`NEO-44` manual QA](../../manual-qa/NEO-44.md)); [server README — Gig progression (NEO-44)](../../../server/README.md#gig-progression-snapshot-neo-44); Bruno `bruno/neon-sprawl-server/gig-progression/`. **NEO-84 landed:** comment-only **`ability_used`** / **`enemy_defeat`** telemetry hook sites in **`CombatOperations.TryResolve`**; reserved **`encounter_start`** / **`player_death`** ([NEO-84](../../plans/NEO-84-implementation-plan.md)); [server README — Combat telemetry hooks (NEO-84)](../../../server/README.md#combat-telemetry-hooks-neo-84). **NEO-85 landed:** client combat HUD — **`ability_cast_client.gd`** parses **`combatResolution`**; **`combat_targets_client.gd`**, **`CastFeedbackLabel`** resolution copy, **`CombatTargetHpLabel`**; event-driven GET refresh ([NEO-85](../../plans/NEO-85-implementation-plan.md), [`NEO-85` manual QA](../../manual-qa/NEO-85.md)); `client/README.md` combat HUD section. **NEO-86 landed:** playable combat capstone — **`gig_progression_client.gd`**, **`GigXpLabel`**, defeat-triggered gig GET refresh; capstone manual QA [`NEO-86`](../../manual-qa/NEO-86.md); `client/README.md` end-to-end combat loop section ([NEO-86](../../plans/NEO-86-implementation-plan.md)). **Epic 5 Slice 1 client capstone complete.** **Backlog decomposed:** Epic 5 Slice 1 — **E5M1-01**–**E5M1-12** landed. **Precursor landed:** E1.M4 cast funnel (NEO-28/31/32). | [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md), [E5_M1](E5_M1_CombatRulesEngine.md), [NEO-77](../../plans/NEO-77-implementation-plan.md), [NEO-79](../../plans/NEO-79-implementation-plan.md), [NEO-78](../../plans/NEO-78-implementation-plan.md), [NEO-80](../../plans/NEO-80-implementation-plan.md), [NEO-81](../../plans/NEO-81-implementation-plan.md), [NEO-82](../../plans/NEO-82-implementation-plan.md), [NEO-83](../../plans/NEO-83-implementation-plan.md), [NEO-44](../../plans/NEO-44-implementation-plan.md), [NEO-84](../../plans/NEO-84-implementation-plan.md), [NEO-85](../../plans/NEO-85-implementation-plan.md), [NEO-86](../../plans/NEO-86-implementation-plan.md), [E1M4-prototype-backlog.md](../../plans/E1M4-prototype-backlog.md) | | E5.M2 | Ready | **NEO-87 landed:** frozen prototype three-behavior catalog in [`content/npc-behaviors/prototype_npc_behaviors.json`](../../../content/npc-behaviors/prototype_npc_behaviors.json); [`npc-behavior-def.schema.json`](../../../content/schemas/npc-behavior-def.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) E5M2 three-id gate ([NEO-87](../../plans/NEO-87-implementation-plan.md)). **NEO-88 landed:** fail-fast server load of `content/npc-behaviors/*_npc_behaviors.json` at startup — `server/NeonSprawl.Server/Game/Npc/` ([NEO-88](../../plans/NEO-88-implementation-plan.md)); [server README — NPC behavior catalog](../../../server/README.md#npc-behavior-catalog-contentnpc-behaviors-neo-88). **NEO-89 landed:** injectable **`INpcBehaviorDefinitionRegistry`** + DI; **`PrototypeNpcBehaviorRegistry`** id constants ([NEO-89](../../plans/NEO-89-implementation-plan.md)). **NEO-90 landed:** **`GET /game/world/npc-behavior-definitions`** — `NpcBehaviorDefinitionsWorldApi` + DTOs in `Game/Npc/` ([NEO-90](../../plans/NEO-90-implementation-plan.md)); [server README — NPC behavior definitions (NEO-90)](../../../server/README.md#npc-behavior-definitions-neo-90); Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`. **NEO-91 landed:** **`PrototypeNpcRegistry`** + combat-target migration — three NPC instance ids replace alpha/beta; per-archetype max HP from behavior catalog ([NEO-91](../../plans/NEO-91-implementation-plan.md)); [server README — Combat entity health (NEO-91)](../../../server/README.md#combat-entity-health-neo-80-neo-91). **NEO-92 landed:** **`IThreatStateStore`** + **`AggroOperations`** — first-hit acquire on damaging cast, leash clear on move/cast; cast/move/fixture hooks ([NEO-92](../../plans/NEO-92-implementation-plan.md)); [server README — Threat / aggro state (NEO-92)](../../../server/README.md#threat--aggro-state-neo-92). **NEO-93 landed:** **`INpcRuntimeStateStore`** + **`NpcRuntimeOperations.AdvanceAll`** — catalog-driven behavior state machine, lazy tick advance with delta cap; dev fixture reset ([NEO-93](../../plans/NEO-93-implementation-plan.md)); [server README — NPC runtime behavior state (NEO-93)](../../../server/README.md#npc-runtime-behavior-state-neo-93). **NEO-94 landed:** **`GET /game/world/npc-runtime-snapshot`** — `NpcRuntimeSnapshotWorldApi` + DTOs; lazy **`AdvanceAll`** on poll; nested **`activeTelegraph`** with server-computed **`windupRemainingSeconds`** ([NEO-94](../../plans/NEO-94-implementation-plan.md)); [server README — NPC runtime snapshot (NEO-94)](../../../server/README.md#npc-runtime-snapshot-neo-94); Bruno `bruno/neon-sprawl-server/npc-runtime-snapshot/`. **NEO-95 landed:** **`IPlayerCombatHealthStore`** + **`NpcAttackOperations.TryResolveTelegraphComplete`** — telegraph resolve via behavior **`attackAbilityId`** + ability **`maxRange`** / **`baseDamage`** ([NEO-95](../../plans/NEO-95-implementation-plan.md), [NEO-98](../../plans/NEO-98-implementation-plan.md)); [server README — Session player combat HP (NEO-95)](../../../server/README.md#session-player-combat-hp-neo-95); Bruno `bruno/neon-sprawl-server/combat-health/`. **NEO-96 landed:** comment-only **`telegraph_fired`** + **`npc_state_transition`** hook sites in **`NpcRuntimeOperations.AdvanceAll`** ([NEO-96](../../plans/NEO-96-implementation-plan.md)); [server README — NPC runtime telemetry hooks (NEO-96)](../../../server/README.md#npc-runtime-telemetry-hooks-neo-96). **NEO-97 landed:** client telegraph HUD — **`npc_runtime_client.gd`**, **`player_combat_health_client.gd`**, **`npc_runtime_hud_state.gd`**, **`TelegraphLabel`** / **`NpcStateLabel`** / **`PlayerCombatHpLabel`**; combat-active ~1 Hz poll ([NEO-97](../../plans/NEO-97-implementation-plan.md), [`NEO-97` manual QA](../../manual-qa/NEO-97.md)); [client README — NPC runtime + telegraph HUD (NEO-97)](../../../client/README.md#npc-runtime--telegraph-hud-neo-97). **NEO-98 landed:** playable NPC telegraph combat capstone — [`NEO-98` manual QA](../../manual-qa/NEO-98.md); `client/README.md` end-to-end NPC telegraph combat loop section ([NEO-98](../../plans/NEO-98-implementation-plan.md)). **NEO-98 server integration:** defeat clears aggro/runtime (**`TryStopOnTargetDefeat`**); telegraph damage range-gated via [`prototype_npc_abilities.json`](../../../content/abilities/prototype_npc_abilities.json) + behavior **`attackAbilityId`**. **Epic 5 Slice 2 client capstone complete.** **Backlog decomposed:** Epic 5 Slice 2 — [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md) **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) **landed**. Three archetypes (`prototype_melee_pressure`, `prototype_ranged_control`, `prototype_elite_mini_boss`); replaces alpha/beta dummies with **`prototype_npc_melee` / `ranged` / `elite`**. Owns **`ThreatState`**, **`TelegraphEvent`**, **`GET …/npc-runtime-snapshot`**, session **`IPlayerCombatHealthStore`**. Upstream **E5.M1 Ready**. | [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md), [E5_M2](E5_M2_NpcAiAndBehaviorProfiles.md), [NEO-87](../../plans/NEO-87-implementation-plan.md), [NEO-88](../../plans/NEO-88-implementation-plan.md), [NEO-89](../../plans/NEO-89-implementation-plan.md), [NEO-90](../../plans/NEO-90-implementation-plan.md), [NEO-91](../../plans/NEO-91-implementation-plan.md), [NEO-92](../../plans/NEO-92-implementation-plan.md), [NEO-93](../../plans/NEO-93-implementation-plan.md), [NEO-94](../../plans/NEO-94-implementation-plan.md), [NEO-95](../../plans/NEO-95-implementation-plan.md), [NEO-96](../../plans/NEO-96-implementation-plan.md), [NEO-97](../../plans/NEO-97-implementation-plan.md), [NEO-98](../../plans/NEO-98-implementation-plan.md), label **`E5.M2`** on NEO-87–NEO-98 | | E5.M3 | Ready | **E5M3-01 catalog landed ([NEO-100](https://linear.app/neon-sprawl/issue/NEO-100)):** encounter + reward-table schemas, `prototype_encounters.json`, `prototype_reward_tables.json`, CI gates. **E5M3-02 server load ([NEO-101](https://linear.app/neon-sprawl/issue/NEO-101)):** fail-fast startup loaders for encounter + reward-table catalogs (CI parity). **E5M3-03 registries ([NEO-102](https://linear.app/neon-sprawl/issue/NEO-102)):** `IEncounterDefinitionRegistry` + `IRewardTableDefinitionRegistry` + DI. **E5M3-04 HTTP read ([NEO-103](https://linear.app/neon-sprawl/issue/NEO-103)):** **`GET /game/world/encounter-definitions`** — `EncounterDefinitionsWorldApi` + nested **`rewardTable`** summary ([NEO-103](../../plans/NEO-103-implementation-plan.md)); [server README — Encounter definitions (NEO-103)](../../../server/README.md#encounter-definitions-neo-103); Bruno `bruno/neon-sprawl-server/encounter-definitions/`. **E5M3-05 stores ([NEO-104](https://linear.app/neon-sprawl/issue/NEO-104)):** **`IEncounterProgressStore`** + **`IEncounterCompletionStore`** + **`EncounterProgressOperations`** ([NEO-104](../../plans/NEO-104-implementation-plan.md)); [server README — Encounter progress (NEO-104)](../../../server/README.md#encounter-progress--completion-stores-neo-104). **E5M3-06 completion grants ([NEO-105](https://linear.app/neon-sprawl/issue/NEO-105)):** **`EncounterCompletionOperations`** + **`EncounterCompleteEvent`** result payload ([NEO-105](../../plans/NEO-105-implementation-plan.md)); [server README — Encounter completion (NEO-105)](../../../server/README.md#encounter-completion--inventory-grants-neo-105). **E5M3-07 combat wiring ([NEO-106](https://linear.app/neon-sprawl/issue/NEO-106)):** **`EncounterCombatWiring`** on **`AbilityCastApi`** ([NEO-106](../../plans/NEO-106-implementation-plan.md)); [server README — Encounter combat wiring (NEO-106)](../../../server/README.md#encounter-combat-wiring-neo-106). **E5M3-09 event record ([NEO-107](https://linear.app/neon-sprawl/issue/NEO-107)):** **`IEncounterCompleteEventStore`** + E7.M2 hook stub ([NEO-107](../../plans/NEO-107-implementation-plan.md)); [server README — Encounter complete event (NEO-107)](../../../server/README.md#encounter-complete-event-record-neo-107). **E5M3-08 per-player GET ([NEO-108](https://linear.app/neon-sprawl/issue/NEO-108)):** **`GET /game/players/{id}/encounter-progress`** — `EncounterProgressApi` + DTOs ([NEO-108](../../plans/NEO-108-implementation-plan.md)); [server README — Per-player encounter progress (NEO-108)](../../../server/README.md#per-player-encounter-progress-neo-108); Bruno `bruno/neon-sprawl-server/encounter-progress/`. **E5M3-10 telemetry ([NEO-109](https://linear.app/neon-sprawl/issue/NEO-109)):** comment-only **`encounter_start`**, **`encounter_complete`**, **`reward_attribution`**, **`encounter_complete_denied`** in **`EncounterProgressOperations`** / **`EncounterCompletionOperations`** ([NEO-109](../../plans/NEO-109-implementation-plan.md)); [server README — Encounter telemetry hooks (NEO-109)](../../../server/README.md#encounter-telemetry-hooks-neo-109). **E5M3-11 client HUD ([NEO-110](https://linear.app/neon-sprawl/issue/NEO-110)):** **`encounter_progress_client.gd`**, **`EncounterProgressLabel`** / **`EncounterCompleteLabel`**; boot + defeat-triggered GET; inventory refresh on **`completed`** ([NEO-110](../../plans/NEO-110-implementation-plan.md), [`NEO-110` manual QA](../../manual-qa/NEO-110.md)); [client README — Encounter progress + loot HUD (NEO-110)](../../../client/README.md#encounter-progress--loot-hud-neo-110). **E5M3-12 client capstone ([NEO-111](https://linear.app/neon-sprawl/issue/NEO-111)):** playable encounter clear loop — [`NEO-111` manual QA](../../manual-qa/NEO-111.md); [client README — End-to-end encounter clear loop (NEO-111)](../../../client/README.md#end-to-end-encounter-clear-loop-neo-111); plan [NEO-111](../../plans/NEO-111-implementation-plan.md). **Epic 5 Slice 3 client capstone complete.** Epic 5 Slice 3 backlog [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md) **E5M3-01** [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) → **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) **landed**. Prototype spine: **`prototype_combat_pocket`** → **`prototype_combat_pocket_clear`**; idempotent completion; per-defeat gig XP unchanged ([NEO-44](../../plans/NEO-44-implementation-plan.md)). Upstream **E5.M2 Ready**, **E3.M3** inventory landed. | [NEO-111 plan](../../plans/NEO-111-implementation-plan.md), [NEO-110 plan](../../plans/NEO-110-implementation-plan.md), [NEO-109 plan](../../plans/NEO-109-implementation-plan.md), [NEO-108 plan](../../plans/NEO-108-implementation-plan.md), [NEO-107 plan](../../plans/NEO-107-implementation-plan.md), [NEO-106 plan](../../plans/NEO-106-implementation-plan.md), [NEO-105 plan](../../plans/NEO-105-implementation-plan.md), [NEO-104 plan](../../plans/NEO-104-implementation-plan.md), [NEO-103 plan](../../plans/NEO-103-implementation-plan.md), [NEO-102 plan](../../plans/NEO-102-implementation-plan.md), [E5_M3](E5_M3_EncounterAndRewardTables.md), label **`E5.M3`** on NEO-100–NEO-111 | -| E7.M2 | Planned | **E7M2-01 catalog landed ([NEO-124](https://linear.app/neon-sprawl/issue/NEO-124)):** `quest-reward-bundle` + `quest-skill-xp-grant` schemas; **`completionRewardBundle`** on four frozen E7.M1 quests; CI bundle freeze + cross-ref gates. **E7M2-02 server load ([NEO-125](https://linear.app/neon-sprawl/issue/NEO-125)):** fail-fast quest catalog **`completionRewardBundle`** validation at startup — `PrototypeE7M2QuestCatalogRules` + bundle schema `$ref` registration ([NEO-125](../../plans/NEO-125-implementation-plan.md)); [server README — Quest catalog (NEO-125)](../../../server/README.md#quest-catalog-contentquests-neo-113). **E7M2-03 delivery store ([NEO-126](https://linear.app/neon-sprawl/issue/NEO-126)):** idempotent **`IRewardDeliveryStore`** + **`RewardDeliveryEvent`** in `Game/Rewards/` — in-memory prototype ([NEO-126](../../plans/NEO-126-implementation-plan.md)); [server README — Reward delivery store (NEO-126)](../../../server/README.md#reward-delivery-store-neo-126). **E7M2-04 router apply ([NEO-127](https://linear.app/neon-sprawl/issue/NEO-127)):** **`RewardRouterOperations.TryDeliverQuestCompletion`** — item + skill XP bundle apply with compensating rollback + store record ([NEO-127](../../plans/NEO-127-implementation-plan.md)); [server README — Reward router (NEO-127)](../../../server/README.md#reward-router-neo-127). **E7M2-05 quest-state wiring ([NEO-128](https://linear.app/neon-sprawl/issue/NEO-128)):** **`QuestStateOperations.TryMarkComplete`** + **`QuestObjectiveWiring`** deliver-then-mark via router ([NEO-128](../../plans/NEO-128-implementation-plan.md)); [server README — Quest state operations (NEO-117)](../../../server/README.md#quest-state-operations-neo-117). **E7M2-06 HTTP read ([NEO-129](https://linear.app/neon-sprawl/issue/NEO-129)):** **`GET …/quest-progress`** **`completionRewardSummary`** from **`IRewardDeliveryStore`** ([NEO-129](../../plans/NEO-129-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/quest-progress/Get quest progress after gather intro complete.bru`. **E7M2-07 telemetry ([NEO-130](https://linear.app/neon-sprawl/issue/NEO-130)):** comment-only **`reward_delivery`** + **`unlock_granted`** stub hook sites in **`RewardRouterOperations.TryDeliverQuestCompletion`** ([NEO-130](../../plans/NEO-130-implementation-plan.md)); [server README — Reward telemetry hooks (NEO-130)](../../../server/README.md#reward-telemetry-hooks-neo-130). **E7M2-08 client HUD ([NEO-131](https://linear.app/neon-sprawl/issue/NEO-131)):** Godot **`QuestRewardDeliveryLabel`** paints **`completionRewardSummary`** on in-session completion transition ([NEO-131](../../plans/NEO-131-implementation-plan.md)); [client README — Quest completion reward HUD (NEO-131)](../../../client/README.md#quest-completion-reward-hud-neo-131). **Backlog decomposed:** Epic 7 Slice 2 — [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md) **E7M2-03** [NEO-126](https://linear.app/neon-sprawl/issue/NEO-126) → **E7M2-09** [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132); label **`E7.M2`**. Client capstone **E7M2-09** [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132). **NEO-43 prep landed:** **`MissionRewardSkillXpGrant`**. **Encounter loot unchanged** (E5.M3 direct grants). Upstream: E7.M1 **Ready**, E2.M2 grant stack, E3.M3 inventory **Ready**. Register row stays **Planned** until capstone **NEO-132**. | [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md), [E7_M2](E7_M2_RewardAndUnlockRouter.md), [NEO-124](../../plans/NEO-124-implementation-plan.md), [NEO-125](../../plans/NEO-125-implementation-plan.md), [NEO-126](../../plans/NEO-126-implementation-plan.md), [NEO-127](../../plans/NEO-127-implementation-plan.md), [NEO-128](../../plans/NEO-128-implementation-plan.md), [NEO-129](../../plans/NEO-129-implementation-plan.md), [NEO-130](../../plans/NEO-130-implementation-plan.md), [NEO-131](../../plans/NEO-131-implementation-plan.md), [NEO-43](../../plans/NEO-43-implementation-plan.md), label **`E7.M2`** on NEO-124–NEO-132 | +| E7.M2 | Ready | **E7M2-01 catalog landed ([NEO-124](https://linear.app/neon-sprawl/issue/NEO-124)):** `quest-reward-bundle` + `quest-skill-xp-grant` schemas; **`completionRewardBundle`** on four frozen E7.M1 quests; CI bundle freeze + cross-ref gates. **E7M2-02 server load ([NEO-125](https://linear.app/neon-sprawl/issue/NEO-125)):** fail-fast quest catalog **`completionRewardBundle`** validation at startup — `PrototypeE7M2QuestCatalogRules` + bundle schema `$ref` registration ([NEO-125](../../plans/NEO-125-implementation-plan.md)); [server README — Quest catalog (NEO-125)](../../../server/README.md#quest-catalog-contentquests-neo-113). **E7M2-03 delivery store ([NEO-126](https://linear.app/neon-sprawl/issue/NEO-126)):** idempotent **`IRewardDeliveryStore`** + **`RewardDeliveryEvent`** in `Game/Rewards/` — in-memory prototype ([NEO-126](../../plans/NEO-126-implementation-plan.md)); [server README — Reward delivery store (NEO-126)](../../../server/README.md#reward-delivery-store-neo-126). **E7M2-04 router apply ([NEO-127](https://linear.app/neon-sprawl/issue/NEO-127)):** **`RewardRouterOperations.TryDeliverQuestCompletion`** — item + skill XP bundle apply with compensating rollback + store record ([NEO-127](../../plans/NEO-127-implementation-plan.md)); [server README — Reward router (NEO-127)](../../../server/README.md#reward-router-neo-127). **E7M2-05 quest-state wiring ([NEO-128](https://linear.app/neon-sprawl/issue/NEO-128)):** **`QuestStateOperations.TryMarkComplete`** + **`QuestObjectiveWiring`** deliver-then-mark via router ([NEO-128](../../plans/NEO-128-implementation-plan.md)); [server README — Quest state operations (NEO-117)](../../../server/README.md#quest-state-operations-neo-117). **E7M2-06 HTTP read ([NEO-129](https://linear.app/neon-sprawl/issue/NEO-129)):** **`GET …/quest-progress`** **`completionRewardSummary`** from **`IRewardDeliveryStore`** ([NEO-129](../../plans/NEO-129-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/quest-progress/Get quest progress after gather intro complete.bru`. **E7M2-07 telemetry ([NEO-130](https://linear.app/neon-sprawl/issue/NEO-130)):** comment-only **`reward_delivery`** + **`unlock_granted`** stub hook sites in **`RewardRouterOperations.TryDeliverQuestCompletion`** ([NEO-130](../../plans/NEO-130-implementation-plan.md)); [server README — Reward telemetry hooks (NEO-130)](../../../server/README.md#reward-telemetry-hooks-neo-130). **E7M2-08 client HUD ([NEO-131](https://linear.app/neon-sprawl/issue/NEO-131)):** Godot **`QuestRewardDeliveryLabel`** paints **`completionRewardSummary`** on in-session completion transition ([NEO-131](../../plans/NEO-131-implementation-plan.md)); [client README — Quest completion reward HUD (NEO-131)](../../../client/README.md#quest-completion-reward-hud-neo-131). **E7M2-09 client capstone ([NEO-132](https://linear.app/neon-sprawl/issue/NEO-132)):** playable quest reward delivery loop — [`NEO-132` manual QA](../../manual-qa/NEO-132.md); [client README — End-to-end quest reward loop (NEO-132)](../../../client/README.md#end-to-end-quest-reward-loop-neo-132); plan [NEO-132](../../plans/NEO-132-implementation-plan.md). **Epic 7 Slice 2 client capstone complete.** Backlog **E7M2-01** [NEO-124](https://linear.app/neon-sprawl/issue/NEO-124) → **E7M2-09** [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132) **landed**. **NEO-43 prep landed:** **`MissionRewardSkillXpGrant`**. **Encounter loot unchanged** (E5.M3 direct grants). Upstream: E7.M1 **Ready**, E2.M2 grant stack, E3.M3 inventory **Ready**. | [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md), [E7_M2](E7_M2_RewardAndUnlockRouter.md), [NEO-124](../../plans/NEO-124-implementation-plan.md), [NEO-125](../../plans/NEO-125-implementation-plan.md), [NEO-126](../../plans/NEO-126-implementation-plan.md), [NEO-127](../../plans/NEO-127-implementation-plan.md), [NEO-128](../../plans/NEO-128-implementation-plan.md), [NEO-129](../../plans/NEO-129-implementation-plan.md), [NEO-130](../../plans/NEO-130-implementation-plan.md), [NEO-131](../../plans/NEO-131-implementation-plan.md), [NEO-132](../../plans/NEO-132-implementation-plan.md), [NEO-43](../../plans/NEO-43-implementation-plan.md), label **`E7.M2`** on NEO-124–NEO-132 | | E7.M1 | Ready | **E7M1-01 catalog landed ([NEO-112](https://linear.app/neon-sprawl/issue/NEO-112)):** quest schemas, `prototype_quests.json`, CI gates (four frozen quest ids, objective cross-refs, acyclic prerequisites, chain terminal token). **E7M1-02 server load ([NEO-113](https://linear.app/neon-sprawl/issue/NEO-113)):** fail-fast startup load of `content/quests/*_quests.json` — `server/NeonSprawl.Server/Game/Quests/` ([NEO-113](../../plans/NEO-113-implementation-plan.md)); [server README — Quest catalog](../../../server/README.md#quest-catalog-contentquests-neo-113). **E7M1-03 registry ([NEO-114](https://linear.app/neon-sprawl/issue/NEO-114)):** **`IQuestDefinitionRegistry`** + DI ([NEO-114](../../plans/NEO-114-implementation-plan.md)). **E7M1-04 HTTP read ([NEO-115](https://linear.app/neon-sprawl/issue/NEO-115)):** **`GET /game/world/quest-definitions`** — `QuestDefinitionsWorldApi` + DTOs ([NEO-115](../../plans/NEO-115-implementation-plan.md)); [server README — Quest definitions (NEO-115)](../../../server/README.md#quest-definitions-neo-115); Bruno `bruno/neon-sprawl-server/quest-definitions/`. **E7M1-05 store ([NEO-116](https://linear.app/neon-sprawl/issue/NEO-116)):** **`IPlayerQuestStateStore`** + in-memory/Postgres persistence ([NEO-116](../../plans/NEO-116-implementation-plan.md)); [server README — Quest progress store (NEO-116)](../../../server/README.md#quest-progress-store-neo-116). **E7M1-06 operations ([NEO-117](https://linear.app/neon-sprawl/issue/NEO-117)):** **`QuestStateOperations`** + reason codes ([NEO-117](../../plans/NEO-117-implementation-plan.md)); [server README — Quest state operations (NEO-117)](../../../server/README.md#quest-state-operations-neo-117). **E7M1-07 objective wiring ([NEO-118](https://linear.app/neon-sprawl/issue/NEO-118)):** **`QuestObjectiveWiring`** on gather/craft/encounter + **`inventory_has_item`** snapshot passes ([NEO-118](../../plans/NEO-118-implementation-plan.md)); [server README — Quest objective wiring (NEO-118)](../../../server/README.md#quest-objective-wiring-neo-118). **E7M1-08 per-player GET ([NEO-119](https://linear.app/neon-sprawl/issue/NEO-119)):** **`GET /game/players/{id}/quest-progress`** — `QuestProgressApi` + DTOs; GET-side inventory refresh ([NEO-119](../../plans/NEO-119-implementation-plan.md)); [server README — Per-player quest progress (NEO-119)](../../../server/README.md#per-player-quest-progress-neo-119); Bruno `bruno/neon-sprawl-server/quest-progress/`. **E7M1-09 accept POST ([NEO-120](https://linear.app/neon-sprawl/issue/NEO-120)):** **`POST /game/players/{id}/quests/{questId}/accept`** — `QuestAcceptApi` + DTOs; **`QuestStateOperations.TryAccept`** ([NEO-120](../../plans/NEO-120-implementation-plan.md)); [server README — Quest accept POST (NEO-120)](../../../server/README.md#quest-accept-post-neo-120); Bruno accept spine in `bruno/neon-sprawl-server/quest-progress/`. **E7M1-10 telemetry ([NEO-121](https://linear.app/neon-sprawl/issue/NEO-121)):** comment-only **`quest_start`**, **`quest_step_complete`**, **`quest_complete`**, **`quest_accept_denied`** hook sites in **`QuestStateOperations`** ([NEO-121](../../plans/NEO-121-implementation-plan.md)); [server README — Quest telemetry hooks (NEO-121)](../../../server/README.md#quest-telemetry-hooks-neo-121). **E7M1-11 client HUD ([NEO-122](https://linear.app/neon-sprawl/issue/NEO-122)):** **`quest_progress_client.gd`**, **`quest_definitions_client.gd`**, **`QuestProgressLabel`** / **`QuestAcceptFeedbackLabel`**; boot + event-driven GET refresh; **Q** / **Shift+Q** accept ([NEO-122](../../plans/NEO-122-implementation-plan.md), [`NEO-122` manual QA](../../manual-qa/NEO-122.md)); [client README — Quest progress + accept HUD (NEO-122)](../../../client/README.md#quest-progress--accept-hud-neo-122). **E7M1-12 client capstone ([NEO-123](https://linear.app/neon-sprawl/issue/NEO-123)):** playable four-quest onboarding chain — [`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.** Backlog **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**. Upstream: E3.M1 gather **Ready**, E3.M2 craft **Ready**, E5.M3 encounter **`contract_handoff_token`** + **`EncounterCompleteEvent`** **Ready**. | [NEO-123 plan](../../plans/NEO-123-implementation-plan.md), [NEO-122 plan](../../plans/NEO-122-implementation-plan.md), [NEO-121 plan](../../plans/NEO-121-implementation-plan.md), [NEO-120 plan](../../plans/NEO-120-implementation-plan.md), [NEO-119 plan](../../plans/NEO-119-implementation-plan.md), [NEO-118 plan](../../plans/NEO-118-implementation-plan.md), [NEO-117 plan](../../plans/NEO-117-implementation-plan.md), [NEO-116 plan](../../plans/NEO-116-implementation-plan.md), [NEO-115 plan](../../plans/NEO-115-implementation-plan.md), [NEO-114 plan](../../plans/NEO-114-implementation-plan.md), [NEO-113 plan](../../plans/NEO-113-implementation-plan.md), [NEO-112 plan](../../plans/NEO-112-implementation-plan.md), [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md), [E7_M1](E7_M1_QuestStateMachine.md), label **`E7.M1`** on NEO-112–NEO-123 | --- diff --git a/docs/decomposition/modules/module_dependency_register.md b/docs/decomposition/modules/module_dependency_register.md index 39302be..1da837b 100644 --- a/docs/decomposition/modules/module_dependency_register.md +++ b/docs/decomposition/modules/module_dependency_register.md @@ -110,9 +110,9 @@ Gameplay **content and curves** default to **boot load** with optional **dev-onl | 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) **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.M2 | RewardAndUnlockRouter | E2.M2, E3.M3, E7.M1 | QuestRewardBundle, UnlockGrant, RewardDeliveryEvent | Prototype | Ready | -**E7.M2 note:** Epic 7 **Slice 2** 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-124](https://linear.app/neon-sprawl/issue/NEO-124) → [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132); label **`E7.M2`**. See [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md), [E7_M2_RewardAndUnlockRouter.md](E7_M2_RewardAndUnlockRouter.md). Upstream: E7.M1 **Ready**, E2.M2 grant stack ([NEO-43](https://linear.app/neon-sprawl/issue/NEO-43)), E3.M3 inventory **Ready**. Client capstone **E7M2-09** [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132). Register row stays **Planned** until capstone lands. +**E7.M2 note:** Epic 7 **Slice 2** 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-124](https://linear.app/neon-sprawl/issue/NEO-124) → [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132) **landed**; label **`E7.M2`**. See [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md), [E7_M2_RewardAndUnlockRouter.md](E7_M2_RewardAndUnlockRouter.md). Upstream: E7.M1 **Ready**, E2.M2 grant stack ([NEO-43](https://linear.app/neon-sprawl/issue/NEO-43)), E3.M3 inventory **Ready**. **E7M2-09 / NEO-132** client capstone landed — [`NEO-132` manual QA](../../manual-qa/NEO-132.md), [client README — End-to-end quest reward loop (NEO-132)](../../../client/README.md#end-to-end-quest-reward-loop-neo-132), plan [NEO-132](../../plans/NEO-132-implementation-plan.md). Epic 7 Slice 2 client capstone complete. | 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 | diff --git a/docs/manual-qa/NEO-123.md b/docs/manual-qa/NEO-123.md index b1db788..1a1a6ea 100644 --- a/docs/manual-qa/NEO-123.md +++ b/docs/manual-qa/NEO-123.md @@ -134,6 +134,7 @@ Adjust gathers/crafts if bag counts differ; counters and craft denies are author - 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)). +- **Slice 2 reward verification:** quest completion bundles + economy idempotency — [NEO-132 manual QA](NEO-132.md) (supersedes for reward delivery AC). ## Acceptance diff --git a/docs/manual-qa/NEO-131.md b/docs/manual-qa/NEO-131.md index d9e9643..d14b2bc 100644 --- a/docs/manual-qa/NEO-131.md +++ b/docs/manual-qa/NEO-131.md @@ -39,3 +39,5 @@ - [NEO-122](NEO-122.md) — quest progress + accept HUD unchanged except new label below accept feedback. - [NEO-110](NEO-110.md) — encounter loot label still independent from quest completion bundles. + +**Full-flow capstone:** [NEO-132](NEO-132.md) extends this checklist with economy HUD verification across all four quest completion bundles. diff --git a/docs/manual-qa/NEO-132.md b/docs/manual-qa/NEO-132.md new file mode 100644 index 0000000..4eb4139 --- /dev/null +++ b/docs/manual-qa/NEO-132.md @@ -0,0 +1,118 @@ +# NEO-132 — Manual QA checklist + +| Field | Value | +|-------|-------| +| Key | NEO-132 | +| Title | E7M2-09: Playable quest reward delivery capstone (Godot) | +| Linear | https://linear.app/neon-sprawl/issue/NEO-132/e7m2-09-playable-quest-reward-delivery-capstone-godot | +| Plan | `docs/plans/NEO-132-implementation-plan.md` | +| Branch | `NEO-132-playable-quest-reward-delivery-capstone` | + +## Preconditions + +- **Fresh dev player:** stop any running server, then start a new instance so in-memory quest progress, **`IRewardDeliveryStore`**, inventory, skill progression, 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-124–NEO-131 reward spine and NEO-123 onboarding capstone landed on `main`. +- **`Economy HUD`** toggle **on** for inventory + skill verification. + +## Quest completion reward freeze (E7M2-01) + +| Quest id | Display name | Bundle grants | Expected **`QuestRewardDeliveryLabel`** | +|----------|--------------|---------------|----------------------------------------| +| `prototype_quest_gather_intro` | Intro: Salvage Run | `salvage` **+25** XP | `Salvage +25 XP` | +| `prototype_quest_combat_intro` | Intro: Clear the Pocket | `salvage` **+25** XP | `Salvage +25 XP` | +| `prototype_quest_refine_intro` | Intro: Refine Stock | `refine` **+25** XP | `Refine +25 XP` | +| `prototype_quest_operator_chain` | Operator Chain | `survey_drone_kit` ×1 + `salvage` **+50** XP | `Survey Drone Kit ×1` + `Salvage +50 XP` | + +**Encounter loot (unchanged — E5.M3):** combat pocket still grants **`scrap_metal_bulk` ×10** + **`contract_handoff_token` ×1** on **`EncounterCompleteLabel`**, not quest bundles. + +## Skill XP verification method + +Record **`SkillProgressionLabel`** salvage/refine **`xp`** values **immediately before** each quest completion transition and **after** the HUD refreshes. Assert the **delta** matches the freeze table (+25 or +50 for salvage; +25 for refine). Gather/craft activity XP may also change totals — deltas per quest completion are authoritative. + +## Expected HUD progression (extends NEO-123) + +| Phase | `QuestProgressLabel` | `QuestRewardDeliveryLabel` | Economy HUD | +|-------|----------------------|----------------------------|-------------| +| Boot | All four **`not started`** | `Quest rewards:` / `—` | salvage/refine xp **0** (fresh server) | +| Gather intro **`completed`** | Salvage Run **`completed`** | `Intro: Salvage Run` + `Salvage +25 XP` | salvage xp **+25** delta | +| Combat intro **`completed`** | Clear the Pocket **`completed`** | `Intro: Clear the Pocket` + `Salvage +25 XP` | salvage xp **+25** delta | +| Refine intro **`completed`** | Refine Stock **`completed`** | `Intro: Refine Stock` + `Refine +25 XP` | refine xp **+25** delta | +| Operator chain **`completed`** | Operator Chain **`completed`** | `Survey Drone Kit ×1` + `Salvage +50 XP` | bag has **`survey_drone_kit`**; salvage xp **+50** delta | +| Godot restart | All four still **`completed`** | `—` (no transition replay) | counts **unchanged** | +| Duplicate accepts | Unchanged | Unchanged | counts **unchanged** | + +## Checklist + +Follow [NEO-123](NEO-123.md) for accept order, anchors, material math, and quest steps **1–15**. **Extend** each completion with reward + economy checks below. + +### Boot + +1. Start server: `cd server/NeonSprawl.Server && dotnet run`. +2. Run Godot main scene (**F5**). Confirm four quests **`not started`**, **`QuestRewardDeliveryLabel`** shows **`Quest rewards:`** / **`—`**, and **`SkillProgressionLabel`** shows salvage/refine xp **0** (fresh server). + +### Intro: Salvage Run (gather + skill XP reward) + +3. Press **Q**; gather at **gamma** **(12, 0)** once (+3 scrap) until **`Intro: Salvage Run: completed`**. +4. Verify **`QuestRewardDeliveryLabel`**: `Intro: Salvage Run` + **`Salvage +25 XP`**. +5. Verify **`SkillProgressionLabel`**: salvage xp increased by **25** vs value recorded before completion (activity gather XP may also apply — quest bundle delta is **+25**). + +### Intro: Clear the Pocket (combat + skill XP reward) + +6. Press **Shift+Q**; defeat all three prototype NPCs (**Tab** + **1**, any order). +7. Verify **`EncounterCompleteLabel`** still lists encounter loot (**`scrap_metal_bulk ×10`**, **`contract_handoff_token ×1`**) — separate from quest bundle. +8. Verify **`Intro: Clear the Pocket: completed`** on **`QuestProgressLabel`**. +9. Verify **`QuestRewardDeliveryLabel`**: `Intro: Clear the Pocket` + **`Salvage +25 XP`**. +10. Verify **`SkillProgressionLabel`**: salvage xp **+25** delta since step 5. + +### Intro: Refine Stock (craft + skill XP reward) + +11. Press **Shift+Q**; craft **`refine_scrap_standard`** when **≥5** scrap in bag. +12. Verify **`Intro: Refine Stock: completed`**. +13. Verify **`QuestRewardDeliveryLabel`**: `Intro: Refine Stock` + **`Refine +25 XP`**. +14. Verify **`SkillProgressionLabel`**: refine xp **+25** delta since step 10. + +### Operator Chain (item + skill XP reward) + +15. Press **Shift+Q**; complete chain steps 1–4 per [NEO-123](NEO-123.md) (gather ×5 → refine → stim → token held). +16. Verify **`Operator Chain: completed`**. +17. Verify **`QuestRewardDeliveryLabel`**: **`Survey Drone Kit ×1`** (or raw id fallback) + **`Salvage +50 XP`**. +18. Verify **`InventoryLabel`** lists **`survey_drone_kit`** (display name when item defs loaded). +19. Verify **`SkillProgressionLabel`**: salvage xp **+50** delta since step 14. + +### Capstone snapshot (record for idempotency) + +20. **Record** final state: + - **`QuestProgressLabel`**: all four **`completed`** + - **`SkillProgressionLabel`**: salvage + refine xp totals + - **`InventoryLabel`**: includes **`survey_drone_kit`**; note scrap/plate/token/stim counts + +### Idempotency (no duplicate reward delivery) + +21. **Godot restart:** stop Godot (**Shift+F5**); **F5** again **without** stopping the server. +22. Verify all four quests still **`completed`**; **`QuestRewardDeliveryLabel`** is **`—`** (transition-only). +23. Verify **`SkillProgressionLabel`** and **`InventoryLabel`** counts **match step 20** exactly. +24. Press **Q** on gather intro. Verify **`Quest accept: denied — already_completed`** (or **`already_active`**). +25. Press **Shift+Q**. Verify **`Quest accept: no eligible quest`** (or equivalent deny). +26. Verify economy HUD counts **still match step 20** — no duplicate skill XP or items. + +### Regression + +27. Component reward HUD: [NEO-131 manual QA](NEO-131.md). +28. Slice 1 quest chain: [NEO-123 manual QA](NEO-123.md). +29. Encounter loot independence: [NEO-110 manual QA](NEO-110.md). + +## Notes + +- **Shift+Q catalog order** matches NEO-123 — combat intro before refine intro after gather completes. +- Quest completion bundles are **idempotent** server-side (`{playerId}:quest_complete:{questId}`); Godot restart must not replay grant copy on **`QuestRewardDeliveryLabel`** (NEO-131 transition-only). +- Economy HUD auto-refreshes on quest completion transition (NEO-132); no manual **I** required for reward verification. +- Epic 7 Slice 2 AC: idempotent reward delivery; replays cannot double-claim ([epic_07 Slice 2](../decomposition/epics/epic_07_quest_faction.md#slice-2---reward-and-unlock-routing)). + +## Acceptance + +- [ ] Steps 1–26 completable in one session without Bruno/curl. +- [ ] All four quest completion bundles visible on **`QuestRewardDeliveryLabel`** during in-session transitions. +- [ ] Skill XP deltas and **`survey_drone_kit`** in bag match freeze table. +- [ ] Godot restart + duplicate accepts leave economy counts unchanged (no double-claim). +- [ ] Epic 7 Slice 2 AC satisfied in Godot. diff --git a/docs/plans/E7M2-prototype-backlog.md b/docs/plans/E7M2-prototype-backlog.md index 171b2ee..2b64a24 100644 --- a/docs/plans/E7M2-prototype-backlog.md +++ b/docs/plans/E7M2-prototype-backlog.md @@ -296,6 +296,8 @@ Combat intro bundle is **skill XP only** — encounter loot already granted **`c - [ ] Epic 7 Slice 2 AC: idempotent reward delivery; replays cannot double-claim. - [ ] Re-read [epic_07 Slice 2 AC](../decomposition/epics/epic_07_quest_faction.md#slice-2---reward-and-unlock-routing). +**Landed ([NEO-132](https://linear.app/neon-sprawl/issue/NEO-132)):** capstone manual QA [`NEO-132`](../../manual-qa/NEO-132.md); [client README — End-to-end quest reward loop (NEO-132)](../../client/README.md#end-to-end-quest-reward-loop-neo-132); economy HUD refresh on quest completion transition; plan [NEO-132](../../plans/NEO-132-implementation-plan.md). + **Client counterpart:** this story **is** the Slice 2 client capstone. --- diff --git a/docs/plans/NEO-132-implementation-plan.md b/docs/plans/NEO-132-implementation-plan.md index e4489c1..3fe9d92 100644 --- a/docs/plans/NEO-132-implementation-plan.md +++ b/docs/plans/NEO-132-implementation-plan.md @@ -54,6 +54,15 @@ - [ ] Epic 7 Slice 2 AC: idempotent reward delivery; replays cannot double-claim. - [ ] Re-read [epic_07 Slice 2 AC](../decomposition/epics/epic_07_quest_faction.md#slice-2---reward-and-unlock-routing). +## Implementation reconciliation (shipped) + +- **`docs/manual-qa/NEO-132.md`** — four-quest capstone extending NEO-123 with reward HUD + economy verification + idempotency steps. +- **`client/README.md`** — **End-to-end quest reward loop (NEO-132)** section with flow table and cross-links. +- **`client/scripts/quest_hud_controller.gd`** — **`quest_completion_reward_transition`** signal on in-session completion with summary. +- **`client/scripts/main.gd`** — connects signal to inventory + skill progression GET refresh (NEO-131 deferred economy HUD gap). +- **`docs/manual-qa/NEO-123.md`**, **`docs/manual-qa/NEO-131.md`** — cross-links to NEO-132 capstone. +- **`docs/plans/E7M2-prototype-backlog.md`**, **`E7_M2_RewardAndUnlockRouter.md`**, **`documentation_and_implementation_alignment.md`**, **`module_dependency_register.md`** — E7M2-09 landed; E7.M2 **Ready**; Epic 7 Slice 2 client capstone complete. + ## Technical approach ### 1. Reward freeze reference (E7M2-01) @@ -142,16 +151,15 @@ Document any fix in **Implementation reconciliation** and add regression note to | Test file | What it covers | |-----------|----------------| -| *(none planned)* | **Docs-primary scope** — server reward spine covered by **`RewardRouterOperationsTests`**, **`QuestStateOperationsTests`**, **`QuestProgressApiTests`**, and content CI; client reward HUD covered by NEO-131 GdUnit suites. Capstone proof is **`docs/manual-qa/NEO-132.md`** human QA. | - -If integration fixes land, add or extend GdUnit only when the fix introduces new client refresh behavior worth locking (follow [testing-expectations.md](../../.cursor/rules/testing-expectations.md) AAA layout). +| `client/test/quest_reward_hud_test.gd` | **Add:** `quest_completion_reward_transition` emitted on in-session completion; not emitted on boot-completed snapshot. | +| *(server unchanged)* | Reward spine covered by **`RewardRouterOperationsTests`**, **`QuestStateOperationsTests`**, **`QuestProgressApiTests`**. Capstone proof is **`docs/manual-qa/NEO-132.md`** human QA. | ## Open questions / risks | Question / risk | Agent recommendation | Status | |-----------------|----------------------|--------| -| **Economy HUD auto-refresh gaps** | Run capstone QA first; if combat/operator-chain completions leave stale labels, add minimal **`main.gd`** refresh hooks in same story — NEO-131 explicitly deferred this to NEO-132. | **deferred** (resolve during implementation QA) | +| **Economy HUD auto-refresh gaps** | Add **`quest_completion_reward_transition`** signal + **`main.gd`** inventory/skill refresh — combat and operator-chain paths lacked auto-refresh. | **adopted** | | **NEO-123 vs NEO-132 relationship** | NEO-132 **supersedes** as Slice 2 capstone for reward idempotency; keep NEO-123 as Slice 1 regression. | **adopted** | | **Manual I key in QA script** | Prefer asserting auto-refresh after each completion; use **I** only as documented workaround if docs-primary QA passes with manual refresh and auto-fix is out of scope. | **deferred** | | **Cumulative skill XP math** | Script records **delta** per completion from **`SkillProgressionLabel`**, not absolute totals — avoids coupling to unrelated XP sources. | **adopted** | -| **Docs-only fails capstone** | Escalate to minimal client refresh fix in same story; record in reconciliation. | **deferred** | +| **Docs-only fails capstone** | Economy refresh hook landed proactively; capstone QA confirms end-to-end. | **adopted** |