Merge pull request #109 from ViPro-Technologies/NEO-75-e3-playable-gather-refine-make-loop
NEO-75: Playable gather→refine→make loop in client (E3S5-04)pull/111/head
commit
7d8bbc722f
|
|
@ -139,7 +139,7 @@ Full checklist: [`docs/manual-qa/NEO-31.md`](../docs/manual-qa/NEO-31.md) · cas
|
|||
- **`GET /game/players/{id}/inventory`** — server-authoritative bag (**24** slots) + equipment stub (**1** slot); see [server README — Player inventory](../server/README.md#player-inventory-neo-54-store-neo-55-http).
|
||||
- **`GET /game/world/item-definitions`** — cached on boot for **`displayName`** labels (NEO-53).
|
||||
- **Scripts:** `scripts/inventory_client.gd`, `scripts/item_definitions_client.gd`; wired from `main.gd` on boot and on **`inventory_refresh`** (**I**).
|
||||
- **HUD:** `UICanvas/InventoryLabel` lists **non-empty bag** rows and **equipment slot 0** (shows **`— empty`** when unoccupied). Failed GET paints **`error — …`** on the label and **`push_warning`** in Output.
|
||||
- **HUD:** `UICanvas/EconomyHudSection/Body/InventoryLabel` lists **non-empty bag** rows and **equipment slot 0** (shows **`— empty`** when unoccupied). Failed GET paints **`error — …`** on the label and **`push_warning`** in Output. **`Economy HUD`** checkbox (NEO-75) collapses the economy block.
|
||||
- **Refresh:** press **I** (or call **`InventoryClient.request_sync_from_server()`** from `main.gd` helpers — entrypoint for NEO-73 gather/craft refresh).
|
||||
|
||||
Full checklist: [`docs/manual-qa/NEO-72.md`](../docs/manual-qa/NEO-72.md).
|
||||
|
|
@ -147,23 +147,42 @@ Full checklist: [`docs/manual-qa/NEO-72.md`](../docs/manual-qa/NEO-72.md).
|
|||
## Gather feedback on interact (NEO-73)
|
||||
|
||||
- **`POST /game/players/{id}/interact`** on **`resource_node`** kinds — server gather engine (NEO-63); no separate gather HTTP on client.
|
||||
- **`GET /game/players/{id}/skill-progression`** — **`salvage`** row on **`SkillProgressionLabel`**; boot hydrate + refresh after successful gather.
|
||||
- **`GET /game/players/{id}/skill-progression`** — **`salvage`** row on **`UICanvas/EconomyHudSection/Body/SkillProgressionLabel`**; boot hydrate + refresh after successful gather.
|
||||
- **Scripts:** `scripts/skill_progression_client.gd`, `scripts/prototype_interactable_picker.gd`; **`interaction_request_client.gd`** emits **`interaction_result_received`**; wired from `main.gd`.
|
||||
- **HUD:** `UICanvas/GatherFeedbackLabel` — success delta **`+N {displayName}`** or deny **`Gather: denied — {reasonCode}`** (including **`node_depleted`**). **`InventoryLabel`** refreshes automatically on successful gather (no **I**).
|
||||
- **HUD:** `UICanvas/GatherFeedbackLabel` — success delta **`+N {displayName}`** or deny **`Gather: denied — {reasonCode}`** (including **`node_depleted`**). **`UICanvas/EconomyHudSection/Body/InventoryLabel`** refreshes automatically on successful gather (no **I**).
|
||||
- **R binding:** nearest in-range **`resource_node`** by horizontal distance to catalog anchors (same radius rule as glow preview).
|
||||
|
||||
Full checklist: [`docs/manual-qa/NEO-73.md`](../docs/manual-qa/NEO-73.md).
|
||||
|
||||
## Craft UI + recipe list (NEO-74)
|
||||
|
||||
- **`GET /game/world/recipe-definitions`** — eight prototype recipes on boot; scrollable panel below **`SkillProgressionLabel`**.
|
||||
- **`GET /game/world/recipe-definitions`** — eight prototype recipes on boot; scrollable panel inside **`EconomyHudSection`** (below **`SkillProgressionLabel`**).
|
||||
- **`POST /game/players/{id}/craft`** — JSON body `schemaVersion`, `recipeId`, `quantity` (default **1**); see [server README — Craft HTTP](../server/README.md).
|
||||
- **Scripts:** `scripts/recipe_definitions_client.gd`, `scripts/craft_client.gd`, `scripts/craft_recipe_panel.gd`; wired from `main.gd`.
|
||||
- **HUD:** `UICanvas/CraftFeedbackLabel` — success **`Craft: +N {displayName}`** from POST **`outputsGranted`**; deny **`Craft: denied — {reasonCode}`**. **`SkillProgressionLabel`** adds **`refine`** row (boot + post-craft refresh). **`InventoryLabel`** refreshes on craft success only (no **I**).
|
||||
- **HUD:** `UICanvas/CraftFeedbackLabel` — success **`Craft: +N {displayName}`** from POST **`outputsGranted`**; deny **`Craft: denied — {reasonCode}`**. **`UICanvas/EconomyHudSection/Body/SkillProgressionLabel`** adds **`refine`** row (boot + post-craft refresh). **`UICanvas/EconomyHudSection/Body/InventoryLabel`** refreshes on craft success only (no **I**).
|
||||
- **Panel:** per-recipe **Craft** button (qty **1**); input summary uses cached item **`displayName`** when available.
|
||||
|
||||
Full checklist: [`docs/manual-qa/NEO-74.md`](../docs/manual-qa/NEO-74.md).
|
||||
|
||||
## End-to-end economy loop (NEO-75)
|
||||
|
||||
Capstone proof that Epic 3 prototype gather→craft works **in Godot** without Bruno.
|
||||
|
||||
**Flow (single session):**
|
||||
|
||||
1. **Boot** — server running; Godot **F5**. Clients hydrate item defs, inventory, skill progression, recipe defs (NEO-72–74).
|
||||
2. **Gather** — **WASD** to each resource anchor; **R** posts nearest in-range **`resource_node`** interact (NEO-63). On success: **`GatherFeedbackLabel`** delta, automatic inventory GET, **salvage** row refresh (NEO-73).
|
||||
3. **Refine** — scroll **`EconomyHudSection`** recipe panel; **Craft** **`refine_scrap_standard`** when **≥5** scrap (NEO-74). On success: **`CraftFeedbackLabel`**, inventory GET, **refine** row refresh.
|
||||
4. **Make** — **Craft** **`make_field_stim_mk0`** when **≥2** **`refined_plate_stock`** and **≥1** scrap. Verify **`field_stim_mk0`** in bag.
|
||||
|
||||
**Inputs:** **R** gather · **Craft** buttons (qty **1**) · **I** manual inventory refresh (optional) · **`Economy HUD`** checkbox collapses inventory/skills/recipe block (NEO-75).
|
||||
|
||||
**Material minimum:** **11** scrap from one **R** at each of four prototype nodes (+1, +2, +3, +5) → two **`refine_scrap_standard`** → one **`make_field_stim_mk0`**.
|
||||
|
||||
**HUD paths:** feedback labels stay on **`UICanvas`**; economy block under **`UICanvas/EconomyHudSection/Body/`** (`InventoryLabel`, `SkillProgressionLabel`, `CraftRecipePanel`).
|
||||
|
||||
Full capstone checklist: [`docs/manual-qa/NEO-75.md`](../docs/manual-qa/NEO-75.md).
|
||||
|
||||
### Manual check (NEO-24)
|
||||
|
||||
1. Run the server and client as in NEO-7 / NEO-9; confirm spawn snap at `(-5, 0.9, -5)`.
|
||||
|
|
@ -223,7 +242,7 @@ On **Linux** (including GitHub Actions), the path must be **`gdUnit4`** with a c
|
|||
|
||||
**CI:** The **GDScript** workflow fails the job if Godot prints **`SCRIPT ERROR:`** or **`ERROR: Failed to load script`** while running GdUnit. GdUnit alone can still exit **0** when a test file fails to parse (that suite is skipped); the workflow guards against that.
|
||||
|
||||
**Scope:** Unit tests cover **`player.gd`**, **`player_locomotion_wasd.gd`** (pure WASD seam helpers), **`position_authority_client.gd`**, **`inventory_client.gd`**, **`item_definitions_client.gd`** (NEO-72), **`skill_progression_client.gd`**, **`prototype_interactable_picker.gd`**, extended **`interaction_request_client_test.gd`**, **`gather_feedback_refresh_test.gd`** (NEO-73), **`craft_client.gd`**, **`recipe_definitions_client.gd`**, **`craft_feedback_refresh_test.gd`** (NEO-74), **`isometric_follow_camera.gd`** (eye math + effective zoom distance), **`camera_state.gd`**, and **`zoom_band_config.gd`**. **`main.gd`** and scene wiring are **not** automated here—use manual checks above.
|
||||
**Scope:** Unit tests cover **`player.gd`**, **`player_locomotion_wasd.gd`** (pure WASD seam helpers), **`position_authority_client.gd`**, **`inventory_client.gd`**, **`item_definitions_client.gd`** (NEO-72), **`skill_progression_client.gd`**, **`prototype_interactable_picker.gd`**, extended **`interaction_request_client_test.gd`**, **`gather_feedback_refresh_test.gd`** (NEO-73), **`craft_client.gd`**, **`recipe_definitions_client.gd`**, **`craft_feedback_refresh_test.gd`** (NEO-74), **`prototype_economy_hud_section.gd`** (NEO-75), **`isometric_follow_camera.gd`** (eye math + effective zoom distance), **`camera_state.gd`**, and **`zoom_band_config.gd`**. **`main.gd`** and scene wiring are **not** automated here—use manual checks above.
|
||||
|
||||
**Camera zoom:** Input actions **`camera_zoom_in`** / **`camera_zoom_out`** (mouse wheel, **`=`** / **`-`**, keypad **+** / **−**) are defined in **`project.godot`**. On layouts where **`+`** requires **Shift**, remap or add a binding under **Project → Project Settings → Input Map** if needed.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
[ext_resource type="Script" path="res://scripts/recipe_definitions_client.gd" id="17_recipe_defs"]
|
||||
[ext_resource type="Script" path="res://scripts/craft_client.gd" id="18_craft"]
|
||||
[ext_resource type="Script" path="res://scripts/craft_recipe_panel.gd" id="19_craft_panel"]
|
||||
[ext_resource type="Script" path="res://scripts/prototype_economy_hud_section.gd" id="20_economy_hud"]
|
||||
|
||||
[sub_resource type="NavigationMesh" id="NavigationMesh_district"]
|
||||
geometry_collision_mask = 1
|
||||
|
|
@ -1211,13 +1212,29 @@ theme_override_font_sizes/font_size = 14
|
|||
autowrap_mode = 3
|
||||
text = "Cooldowns: —"
|
||||
|
||||
[node name="InventoryLabel" type="Label" parent="UICanvas" unique_id=9000007]
|
||||
[node name="EconomyHudSection" type="VBoxContainer" parent="UICanvas" unique_id=9000014]
|
||||
offset_left = 8.0
|
||||
offset_top = 524.0
|
||||
offset_right = 520.0
|
||||
offset_bottom = 672.0
|
||||
offset_bottom = 1056.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
script = ExtResource("20_economy_hud")
|
||||
|
||||
[node name="HeaderRow" type="HBoxContainer" parent="UICanvas/EconomyHudSection" unique_id=9000015]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ToggleButton" type="CheckButton" parent="UICanvas/EconomyHudSection/HeaderRow" unique_id=9000016]
|
||||
layout_mode = 2
|
||||
text = "Economy HUD"
|
||||
|
||||
[node name="Body" type="VBoxContainer" parent="UICanvas/EconomyHudSection" unique_id=9000017]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="InventoryLabel" type="Label" parent="UICanvas/EconomyHudSection/Body" unique_id=9000007]
|
||||
custom_minimum_size = Vector2(512, 148)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_colors/font_color = Color(0.9, 0.86, 0.98, 1)
|
||||
theme_override_colors/font_outline_color = Color(0.08, 0.08, 0.1, 1)
|
||||
theme_override_constants/outline_size = 6
|
||||
|
|
@ -1226,13 +1243,10 @@ autowrap_mode = 3
|
|||
text = "Inventory: (refresh: I)
|
||||
Loading…"
|
||||
|
||||
[node name="SkillProgressionLabel" type="Label" parent="UICanvas" unique_id=9000009]
|
||||
offset_left = 8.0
|
||||
offset_top = 684.0
|
||||
offset_right = 520.0
|
||||
offset_bottom = 768.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
[node name="SkillProgressionLabel" type="Label" parent="UICanvas/EconomyHudSection/Body" unique_id=9000009]
|
||||
custom_minimum_size = Vector2(512, 84)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_colors/font_color = Color(0.82, 0.9, 1, 1)
|
||||
theme_override_colors/font_outline_color = Color(0.08, 0.08, 0.1, 1)
|
||||
theme_override_constants/outline_size = 6
|
||||
|
|
@ -1241,19 +1255,17 @@ autowrap_mode = 3
|
|||
text = "Skills:
|
||||
Loading…"
|
||||
|
||||
[node name="CraftRecipePanel" type="Control" parent="UICanvas" unique_id=9000011]
|
||||
anchors_preset = 0
|
||||
offset_left = 8.0
|
||||
offset_top = 776.0
|
||||
offset_right = 520.0
|
||||
offset_bottom = 1056.0
|
||||
[node name="CraftRecipePanel" type="Control" parent="UICanvas/EconomyHudSection/Body" unique_id=9000011]
|
||||
custom_minimum_size = Vector2(512, 280)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("19_craft_panel")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="UICanvas/CraftRecipePanel" unique_id=9000012]
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="UICanvas/EconomyHudSection/Body/CraftRecipePanel" unique_id=9000012]
|
||||
layout_mode = 0
|
||||
offset_right = 512.0
|
||||
offset_bottom = 280.0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UICanvas/CraftRecipePanel/ScrollContainer" unique_id=9000013]
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UICanvas/EconomyHudSection/Body/CraftRecipePanel/ScrollContainer" unique_id=9000013]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
|
|
|||
|
|
@ -102,11 +102,13 @@ var _recipe_defs_error: String = ""
|
|||
@onready var _target_lock_label: Label = $UICanvas/TargetLockLabel
|
||||
@onready var _cast_feedback_label: Label = $UICanvas/CastFeedbackLabel
|
||||
@onready var _cooldown_slots_label: Label = $UICanvas/CooldownSlotsLabel
|
||||
@onready var _inventory_label: Label = $UICanvas/InventoryLabel
|
||||
@onready var _economy_hud_section: VBoxContainer = $UICanvas/EconomyHudSection
|
||||
@onready var _inventory_label: Label = _economy_hud_section.get_node("Body/InventoryLabel")
|
||||
@onready var _gather_feedback_label: Label = $UICanvas/GatherFeedbackLabel
|
||||
@onready var _craft_feedback_label: Label = $UICanvas/CraftFeedbackLabel
|
||||
@onready var _skill_progression_label: Label = $UICanvas/SkillProgressionLabel
|
||||
@onready var _craft_recipe_panel: Node = $UICanvas/CraftRecipePanel
|
||||
@onready
|
||||
var _skill_progression_label: Label = _economy_hud_section.get_node("Body/SkillProgressionLabel")
|
||||
@onready var _craft_recipe_panel: Node = _economy_hud_section.get_node("Body/CraftRecipePanel")
|
||||
@onready var _inventory_client: Node = $InventoryClient
|
||||
@onready var _item_defs_client: Node = $ItemDefinitionsClient
|
||||
@onready var _skill_progression_client: Node = $SkillProgressionClient
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
extends VBoxContainer
|
||||
|
||||
## NEO-75: collapsible economy HUD block (inventory, skills, craft panel).
|
||||
|
||||
@onready var _toggle: CheckButton = $HeaderRow/ToggleButton
|
||||
@onready var _body: VBoxContainer = $Body
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_toggle.button_pressed = true
|
||||
_body.visible = true
|
||||
_toggle.toggled.connect(_on_toggle_toggled)
|
||||
|
||||
|
||||
func _on_toggle_toggled(pressed: bool) -> void:
|
||||
_body.visible = pressed
|
||||
|
||||
|
||||
func set_expanded(expanded: bool) -> void:
|
||||
_toggle.button_pressed = expanded
|
||||
_body.visible = expanded
|
||||
|
||||
|
||||
func is_body_visible() -> bool:
|
||||
return _body.visible
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bneo75economyhud1
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
extends GdUnitTestSuite
|
||||
|
||||
## NEO-75: economy HUD collapse toggle shows/hides body container.
|
||||
## Covers economy HUD node paths via EconomyHudSection.get_node in main.gd.
|
||||
|
||||
const EconomyHudSection := preload("res://scripts/prototype_economy_hud_section.gd")
|
||||
|
||||
|
||||
func _build_section() -> Node:
|
||||
var section: VBoxContainer = EconomyHudSection.new()
|
||||
var header := HBoxContainer.new()
|
||||
header.name = "HeaderRow"
|
||||
var toggle := CheckButton.new()
|
||||
toggle.name = "ToggleButton"
|
||||
toggle.text = "Economy HUD"
|
||||
header.add_child(toggle)
|
||||
var body := VBoxContainer.new()
|
||||
body.name = "Body"
|
||||
var inventory := Label.new()
|
||||
inventory.name = "InventoryLabel"
|
||||
body.add_child(inventory)
|
||||
section.add_child(header)
|
||||
section.add_child(body)
|
||||
return section
|
||||
|
||||
|
||||
func test_body_visible_when_expanded_by_default() -> void:
|
||||
# Arrange
|
||||
var section := _build_section()
|
||||
add_child(section)
|
||||
await await_idle_frame()
|
||||
# Act
|
||||
var visible: bool = section.call("is_body_visible")
|
||||
# Assert
|
||||
assert_bool(visible).is_true()
|
||||
|
||||
|
||||
func test_toggle_collapses_body() -> void:
|
||||
# Arrange
|
||||
var section := _build_section()
|
||||
add_child(section)
|
||||
await await_idle_frame()
|
||||
# Act
|
||||
section.get_node("HeaderRow/ToggleButton").button_pressed = false
|
||||
await await_idle_frame()
|
||||
# Assert
|
||||
assert_bool(section.call("is_body_visible")).is_false()
|
||||
|
||||
|
||||
func test_toggle_expands_body_again() -> void:
|
||||
# Arrange
|
||||
var section := _build_section()
|
||||
add_child(section)
|
||||
await await_idle_frame()
|
||||
section.call("set_expanded", false)
|
||||
await await_idle_frame()
|
||||
# Act
|
||||
section.call("set_expanded", true)
|
||||
await await_idle_frame()
|
||||
# Assert
|
||||
assert_bool(section.call("is_body_visible")).is_true()
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bneo75economyhudtest1
|
||||
|
|
@ -96,6 +96,8 @@ Implement gathering from world nodes, item/inventory representation, recipe exec
|
|||
|
||||
**Linear backlog:** [E3S5-client-prototype-backlog.md](../../plans/E3S5-client-prototype-backlog.md) — [NEO-72](https://linear.app/neon-sprawl/issue/NEO-72) → [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75).
|
||||
|
||||
**Status:** Slice 5 client capstone landed — [NEO-75](../../manual-qa/NEO-75.md) manual QA; NEO-72→NEO-75 complete; prototype gather→craft loop playable in Godot (not Bruno-only).
|
||||
|
||||
<a id="epic-3-slice-4"></a>
|
||||
|
||||
### Slice 4 - Sinks and economy policy (pre-production)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
| **Module ID** | E3.M1 |
|
||||
| **Epic** | [Epic 3 — Crafting Economy](../epics/epic_03_crafting_economy.md) |
|
||||
| **Stage target** | Prototype |
|
||||
| **Status** | Ready — Slice 2 backlog [NEO-57](https://linear.app/neon-sprawl/issue/NEO-57)–[NEO-64](https://linear.app/neon-sprawl/issue/NEO-64) complete ([E3M1-prototype-backlog](../../plans/E3M1-prototype-backlog.md); NEO-41 anchor below; see [dependency register](module_dependency_register.md)) |
|
||||
| **Status** | Ready — Slice 2 backlog [NEO-57](https://linear.app/neon-sprawl/issue/NEO-57)–[NEO-64](https://linear.app/neon-sprawl/issue/NEO-64) complete ([E3M1-prototype-backlog](../../plans/E3M1-prototype-backlog.md); NEO-41 anchor below; see [dependency register](module_dependency_register.md)); Slice 5 client [NEO-73](https://linear.app/neon-sprawl/issue/NEO-73) gather feedback + capstone [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) playable loop landed |
|
||||
| **Linear** | Label **`E3.M1`** per [decomposition README — Linear alignment](../README.md#linear-alignment) |
|
||||
|
||||
## Purpose
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
| **Module ID** | E3.M2 |
|
||||
| **Epic** | [Epic 3 — Crafting Economy](../epics/epic_03_crafting_economy.md) |
|
||||
| **Stage target** | Prototype |
|
||||
| **Status** | Ready — Slice 3 server backlog [E3M2-prototype-backlog](../../plans/E3M2-prototype-backlog.md): E3M2-01 [NEO-65](https://linear.app/neon-sprawl/issue/NEO-65) through E3M2-07 [NEO-71](https://linear.app/neon-sprawl/issue/NEO-71) landed; Slice 5 client craft UI [NEO-74](https://linear.app/neon-sprawl/issue/NEO-74) landed (capstone [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) remaining) |
|
||||
| **Status** | Ready — Slice 3 server backlog [E3M2-prototype-backlog](../../plans/E3M2-prototype-backlog.md): E3M2-01 [NEO-65](https://linear.app/neon-sprawl/issue/NEO-65) through E3M2-07 [NEO-71](https://linear.app/neon-sprawl/issue/NEO-71) landed; Slice 5 client [NEO-74](https://linear.app/neon-sprawl/issue/NEO-74) craft UI + capstone [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) playable loop landed |
|
||||
|
||||
## Implementation snapshot
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
| **Module ID** | E3.M3 |
|
||||
| **Epic** | [Epic 3 — Crafting Economy](../epics/epic_03_crafting_economy.md) |
|
||||
| **Stage target** | Prototype |
|
||||
| **Status** | In Progress — Slice 1 backlog [NEO-50](https://linear.app/neon-sprawl/issue/NEO-50)–[NEO-56](https://linear.app/neon-sprawl/issue/NEO-56) ([E3M3-prototype-backlog](../../plans/E3M3-prototype-backlog.md); see [dependency register](module_dependency_register.md)) |
|
||||
| **Status** | In Progress — Slice 1 backlog [NEO-50](https://linear.app/neon-sprawl/issue/NEO-50)–[NEO-56](https://linear.app/neon-sprawl/issue/NEO-56) complete ([E3M3-prototype-backlog](../../plans/E3M3-prototype-backlog.md); see [dependency register](module_dependency_register.md)); Slice 5 client [NEO-72](https://linear.app/neon-sprawl/issue/NEO-72) inventory HUD + capstone [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) economy HUD collapse landed |
|
||||
|
||||
## Purpose
|
||||
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ Rows appear when work starts; default for unlisted modules is **Planned** / not
|
|||
| E2.M1 | In Progress | **NEO-33 landed:** frozen prototype trio **`salvage`** / **`refine`** / **`intrusion`** in [`content/skills/prototype_skills.json`](../../../content/skills/prototype_skills.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) enforce schema, duplicate `id`, exact trio ids, and category coverage; designer note on **`allowedXpSourceKinds`** in [E2_M1](E2_M1_SkillDefinitionRegistry.md) + [`content/README.md`](../../../content/README.md). **NEO-34 landed:** fail-fast server load of `content/skills/*.json` at startup — `server/NeonSprawl.Server/Game/Skills/` ([NEO-34](../../plans/NEO-34-implementation-plan.md)); config + discovery in [server README — Skill catalog](../../../server/README.md#skill-catalog-contentskills-neo-34). **NEO-35 landed:** `ISkillDefinitionRegistry` / `SkillDefinitionRegistry` + DI ([NEO-35](../../plans/NEO-35-implementation-plan.md)). **NEO-36 landed:** versioned **`GET /game/world/skill-definitions`** ([NEO-36](../../plans/NEO-36-implementation-plan.md)) — `SkillDefinitionsWorldApi` + `SkillDefinitionsListDtos` in `server/NeonSprawl.Server/Game/Skills/`; Bruno `bruno/neon-sprawl-server/skill-definitions/`; manual QA [`NEO-36`](../../manual-qa/NEO-36.md). **E2.M2 consumer (NEO-38 landed):** grant path validates `skillId` + `sourceKind` vs catalog **`allowedXpSourceKinds`** on **`POST …/skill-progression`** ([NEO-38](../../plans/NEO-38-implementation-plan.md)); see [server README — Skill progression grant](../../../server/README.md#skill-progression-grant-neo-38) and [E2_M2](E2_M2_XpAwardAndLevelEngine.md). | [NEO-33](../../plans/NEO-33-implementation-plan.md), [NEO-34](../../plans/NEO-34-implementation-plan.md), [NEO-35](../../plans/NEO-35-implementation-plan.md), [NEO-36](../../plans/NEO-36-implementation-plan.md); [E2_M1](E2_M1_SkillDefinitionRegistry.md); [module_dependency_register.md](module_dependency_register.md) **E2.M1 note**; `server/NeonSprawl.Server/Game/Skills/`; [`docs/manual-qa/NEO-36.md`](../../manual-qa/NEO-36.md); [server README — Skill definitions (NEO-36)](../../../server/README.md#skill-definitions-neo-36) |
|
||||
| E2.M3 | In Progress | **NEO-45 landed:** prototype **`salvage`** mastery catalog + CI gates (see [NEO-45 plan](../../plans/NEO-45-implementation-plan.md)). **NEO-46 landed:** fail-fast server load under `server/NeonSprawl.Server/Game/Mastery/` — `MasteryCatalogLoader`, `IMasteryCatalogRegistry`, cross-check vs `ISkillDefinitionRegistry`, Slice 4 + **`tierIndex`** 1..N gate; see [NEO-46 plan](../../plans/NEO-46-implementation-plan.md). **NEO-47 landed:** `PerkUnlockEngine`, `IPlayerPerkStateStore` + **`V004`**, level-up hook in skill XP grants; see [NEO-47 plan](../../plans/NEO-47-implementation-plan.md). **NEO-49 landed:** comment-only **`perk_unlock`** telemetry hook site in [`PerkUnlockEngine.TryUnlockPerks`](../../../server/NeonSprawl.Server/Game/Mastery/PerkUnlockEngine.cs) ([NEO-49 plan](../../plans/NEO-49-implementation-plan.md), [`NEO-49` manual QA](../../manual-qa/NEO-49.md)); [server README — Perk unlock telemetry (NEO-49)](../../../server/README.md#perk-unlock-engine-and-telemetry-hooks-neo-47-neo-49). **NEO-48 landed:** **`GET`/`POST /game/players/{id}/perk-state`** — `PerkStateApi` + DTOs in `Game/Mastery/` ([NEO-48](../../plans/NEO-48-implementation-plan.md), [`NEO-48` manual QA](../../manual-qa/NEO-48.md)); [server README — Perk state (NEO-48)](../../../server/README.md#perk-state-neo-48); Bruno `bruno/neon-sprawl-server/perk-state/`. | [NEO-45](../../plans/NEO-45-implementation-plan.md), [NEO-46](../../plans/NEO-46-implementation-plan.md), [NEO-47](../../plans/NEO-47-implementation-plan.md), [NEO-48](../../plans/NEO-48-implementation-plan.md), [NEO-49](../../plans/NEO-49-implementation-plan.md), [E2M3-pre-production-backlog](../../plans/E2M3-pre-production-backlog.md), [E2_M3](E2_M3_MasteryAndPerkUnlocks.md) |
|
||||
| E2.M2 | In Progress | **NEO-37 landed:** versioned **`GET /game/players/{id}/skill-progression`** ([NEO-37](../../plans/NEO-37-implementation-plan.md)) — read model for every registered skill; known-player gate via `IPositionStateStore`; [server README — Skill progression snapshot (NEO-37)](../../../server/README.md#skill-progression-snapshot-neo-37); manual QA [`NEO-37`](../../manual-qa/NEO-37.md). **NEO-38 landed:** **`POST`** same path — grant apply, persistence (`IPlayerSkillProgressionStore`, `V003` migration), structured denies + **`levelUps`** ([NEO-38](../../plans/NEO-38-implementation-plan.md)); manual QA [`NEO-38`](../../manual-qa/NEO-38.md); Bruno `bruno/neon-sprawl-server/skill-progression/`; [server README — Skill progression grant (NEO-38)](../../../server/README.md#skill-progression-grant-neo-38). **NEO-39 landed:** data-driven `LevelCurve` content + schema + CI validation (`*_level_curve.json`) with fail-fast startup schema checks; progression GET/POST level resolution now uses `ISkillLevelCurve` content-backed thresholds ([NEO-39](../../plans/NEO-39-implementation-plan.md)); manual QA [`NEO-39`](../../manual-qa/NEO-39.md). **NEO-40 landed:** comment-only telemetry hook sites in [`SkillProgressionSnapshotApi.cs`](../../../server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs) on **`POST …/skill-progression`** for future **`xp_grant`** / **`level_up`** ([NEO-40](../../plans/NEO-40-implementation-plan.md), [`NEO-40` manual QA](../../manual-qa/NEO-40.md)). **NEO-41 landed:** gather prototype — **`POST …/interact`** with **`kind: resource_node`** grants **`salvage`** + **`activity`** (10 XP) via [`SkillProgressionGrantOperations`](../../../server/NeonSprawl.Server/Game/Skills/SkillProgressionGrantOperations.cs) ([NEO-41](../../plans/NEO-41-implementation-plan.md), [`NEO-41` manual QA](../../manual-qa/NEO-41.md)); [server README — Interaction](../../../server/README.md#interaction-neo-9). **NEO-42 landed (prep):** **`RefineActivitySkillXpGrant`** / **`RefineSkillXpConstants`** + deny-registry factory + tests — same NEO-38 grant stack; **E3.M2** must invoke on craft/refine success ([NEO-42](../../plans/NEO-42-implementation-plan.md), [`NEO-42` manual QA](../../manual-qa/NEO-42.md)); [server README — Craft / refine hook (NEO-42)](../../../server/README.md#craft--refine-hook--skill-xp-neo-42). **NEO-43 landed (prep):** **`MissionRewardSkillXpGrant`** / **`MissionRewardSkillXpConstants`** + deny-registry factory + tests — same NEO-38 grant stack with fixed **`sourceKind: mission_reward`**; **E7.M2** must invoke from quest hand-in ([NEO-43](../../plans/NEO-43-implementation-plan.md), [`NEO-43` manual QA](../../manual-qa/NEO-43.md)); [server README — Mission / quest reward (NEO-43)](../../../server/README.md#mission--quest-reward--skill-xp-neo-43). **Slice 3 still open:** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44). See [epic_02 — E2.M2 + Slice 3](../epics/epic_02_skills_and_progression.md). | [NEO-37](../../plans/NEO-37-implementation-plan.md), [NEO-38](../../plans/NEO-38-implementation-plan.md), [NEO-39](../../plans/NEO-39-implementation-plan.md), [NEO-40](../../plans/NEO-40-implementation-plan.md), [NEO-41](../../plans/NEO-41-implementation-plan.md), [NEO-42](../../plans/NEO-42-implementation-plan.md), [NEO-43](../../plans/NEO-43-implementation-plan.md), [E2_M2](E2_M2_XpAwardAndLevelEngine.md); label **`E2.M2`** on NEO-37–NEO-41, NEO-42–NEO-44 |
|
||||
| E3.M1 | Ready | **NEO-41 landed (prototype, superseded on interact by NEO-63):** inline XP on **`resource_node`** interact. **NEO-57–NEO-61** — content, registry, HTTP defs, depletion store (see prior alignment). **NEO-62 landed:** **`GatherOperations`** + **`GatherResult`**. **NEO-63 landed:** **`POST …/interact`** **`resource_node`** → gather engine; **four** **`PrototypeInteractableRegistry`** anchors; Bruno gather → inventory + depletion deny ([NEO-63](../../plans/NEO-63-implementation-plan.md), [`NEO-63` manual QA](../../manual-qa/NEO-63.md)); [server README — Gather via interact (NEO-63)](../../../server/README.md#gather-via-interact-neo-63). **NEO-64 landed:** comment-only **`resource_gathered`** / **`gather_node_depleted`** hook sites in **`GatherOperations.TryGather`** ([NEO-64](../../plans/NEO-64-implementation-plan.md), [`NEO-64` manual QA](../../manual-qa/NEO-64.md)); Epic 3 Slice 2 backlog **E3M1-01**–**E3M1-08** complete. **NEO-73 landed:** client gather feedback — **`skill_progression_client.gd`**, **`prototype_interactable_picker.gd`** (nearest in-range **R**), **`GatherFeedbackLabel`** + **`SkillProgressionLabel`**, auto inventory/skill refresh after successful gather ([NEO-73](../../plans/NEO-73-implementation-plan.md), [`NEO-73` manual QA](../../manual-qa/NEO-73.md)); `client/README.md` gather section. **Slice 5 client (remaining):** capstone [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) — [E3S5 backlog](../../plans/E3S5-client-prototype-backlog.md). | [NEO-41](../../plans/NEO-41-implementation-plan.md) … [NEO-64](../../plans/NEO-64-implementation-plan.md), [NEO-73](../../plans/NEO-73-implementation-plan.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3_M1](E3_M1_ResourceNodeAndGatherLoop.md); `server/NeonSprawl.Server/Game/Gathering/`, `Game/Interaction/`, `client/scripts/skill_progression_client.gd`, `client/scripts/prototype_interactable_picker.gd` |
|
||||
| E3.M3 | In Progress | **NEO-50 landed:** frozen prototype six-item catalog in [`content/items/prototype_items.json`](../../../content/items/prototype_items.json); [`item-def.schema.json`](../../../content/schemas/item-def.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py). **NEO-51 landed:** fail-fast server load of `content/items/*_items.json` at startup — `server/NeonSprawl.Server/Game/Items/` ([NEO-51](../../plans/NEO-51-implementation-plan.md)); [server README — Item catalog](../../../server/README.md#item-catalog-contentitems-neo-51). **NEO-52 landed:** injectable **`IItemDefinitionRegistry`** + lookup tests ([NEO-52](../../plans/NEO-52-implementation-plan.md)). **NEO-53 landed:** **`GET /game/world/item-definitions`** — `ItemDefinitionsWorldApi` + DTOs in `Game/Items/` ([NEO-53](../../plans/NEO-53-implementation-plan.md), [`NEO-53` manual QA](../../manual-qa/NEO-53.md)); [server README — Item definitions (NEO-53)](../../../server/README.md#item-definitions-neo-53); Bruno `bruno/neon-sprawl-server/item-definitions/`. **NEO-54 landed:** **`IPlayerInventoryStore`** + **`PlayerInventoryOperations`** — 24 bag + 1 equipment slots, stack rules, `V005` migration ([NEO-54](../../plans/NEO-54-implementation-plan.md)). **NEO-55 landed:** **`GET`/`POST /game/players/{id}/inventory`** — `PlayerInventoryApi` + DTOs in `Game/Items/` ([NEO-55](../../plans/NEO-55-implementation-plan.md)); [server README — Player inventory (NEO-54 store, NEO-55 HTTP)](../../../server/README.md#player-inventory-neo-54-store-neo-55-http); Bruno `bruno/neon-sprawl-server/inventory/`. **NEO-56 landed:** comment-only **`item_created`** / **`inventory_transfer_denied`** telemetry hook sites in [`PlayerInventoryOperations`](../../../server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs) ([NEO-56](../../plans/NEO-56-implementation-plan.md), [`NEO-56` manual QA](../../manual-qa/NEO-56.md)); no E9.M1 ingest. **NEO-72 landed:** client inventory HUD — **`inventory_client.gd`**, **`item_definitions_client.gd`**, **`InventoryLabel`**, boot hydrate + **`I`** refresh ([NEO-72](../../plans/NEO-72-implementation-plan.md), [`NEO-72` manual QA](../../manual-qa/NEO-72.md)); `client/README.md` inventory HUD section. **Slice 5 client (remaining):** capstone [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) — [E3S5 backlog](../../plans/E3S5-client-prototype-backlog.md). | [NEO-50](../../plans/NEO-50-implementation-plan.md), [NEO-51](../../plans/NEO-51-implementation-plan.md), [NEO-52](../../plans/NEO-52-implementation-plan.md), [NEO-53](../../plans/NEO-53-implementation-plan.md), [NEO-54](../../plans/NEO-54-implementation-plan.md), [NEO-55](../../plans/NEO-55-implementation-plan.md), [NEO-56](../../plans/NEO-56-implementation-plan.md), [NEO-72](../../plans/NEO-72-implementation-plan.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3M3-prototype-backlog](../../plans/E3M3-prototype-backlog.md), [E3_M3](E3_M3_ItemizationAndInventorySchema.md) |
|
||||
| E3.M2 | Ready | **NEO-65 landed:** frozen prototype eight-recipe catalog in [`content/recipes/prototype_recipes.json`](../../../content/recipes/prototype_recipes.json); [`recipe-def.schema.json`](../../../content/schemas/recipe-def.schema.json) + [`recipe-io-row.schema.json`](../../../content/schemas/recipe-io-row.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) Slice 3 gates ([NEO-65](../../plans/NEO-65-implementation-plan.md)). **NEO-66 landed:** fail-fast server load of `content/recipes/*_recipes.json` at startup — `server/NeonSprawl.Server/Game/Crafting/` ([NEO-66](../../plans/NEO-66-implementation-plan.md)); [server README — Recipe catalog](../../../server/README.md#recipe-catalog-contentrecipes-neo-66). **NEO-67 landed:** injectable **`IRecipeDefinitionRegistry`** + lookup tests ([NEO-67](../../plans/NEO-67-implementation-plan.md)). **NEO-68 landed:** **`GET /game/world/recipe-definitions`** — `RecipeDefinitionsWorldApi` + DTOs in `Game/Crafting/` ([NEO-68](../../plans/NEO-68-implementation-plan.md), [`NEO-68` manual QA](../../manual-qa/NEO-68.md)); [server README — Recipe definitions (NEO-68)](../../../server/README.md#recipe-definitions-neo-68); Bruno `bruno/neon-sprawl-server/recipe-definitions/`. **NEO-69 landed:** **`CraftOperations.TryCraft`** + **`CraftResult`** — pre-flight inputs/outputs, inventory mutation, refine XP ([NEO-69](../../plans/NEO-69-implementation-plan.md)); [server README — Craft engine (NEO-69)](../../../server/README.md#craft-engine-neo-69-and-craft-http-neo-70). **NEO-70 landed:** **`POST /game/players/{id}/craft`** — `PlayerCraftApi` + wire **`CraftResponse`**; Bruno gather→refine→make spine ([NEO-70](../../plans/NEO-70-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/craft/`. **NEO-71 landed:** comment-only **`item_crafted`** / **`craft_failed`** telemetry hook sites in **`CraftOperations.TryCraft`** ([NEO-71](../../plans/NEO-71-implementation-plan.md)); [server README — Craft telemetry hooks (NEO-71)](../../../server/README.md#craft-telemetry-hooks-neo-71). **NEO-42 landed (prep):** **`RefineActivitySkillXpGrant`** / **`RefineSkillXpConstants`** — wired from craft engine success path. Epic 3 Slice 3 server backlog **E3M2-01**–**E3M2-07** complete. Upstream: E3.M1 **Ready**, E3.M3 inventory landed. **NEO-74 landed:** client craft UI — **`recipe_definitions_client.gd`**, **`craft_client.gd`**, **`craft_recipe_panel.gd`**, **`CraftFeedbackLabel`**, **refine** skill row ([NEO-74](../../plans/NEO-74-implementation-plan.md), [`NEO-74` manual QA](../../manual-qa/NEO-74.md)); `client/README.md` craft section. **Slice 5 client (remaining):** capstone playable loop [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75) — [E3S5 backlog](../../plans/E3S5-client-prototype-backlog.md). | [NEO-42](../../plans/NEO-42-implementation-plan.md), [NEO-65](../../plans/NEO-65-implementation-plan.md), [NEO-66](../../plans/NEO-66-implementation-plan.md), [NEO-67](../../plans/NEO-67-implementation-plan.md), [NEO-68](../../plans/NEO-68-implementation-plan.md), [NEO-69](../../plans/NEO-69-implementation-plan.md), [NEO-70](../../plans/NEO-70-implementation-plan.md), [NEO-71](../../plans/NEO-71-implementation-plan.md), [NEO-74](../../plans/NEO-74-implementation-plan.md) … [NEO-75](https://linear.app/neon-sprawl/issue/NEO-75), [E3M2-prototype-backlog](../../plans/E3M2-prototype-backlog.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3_M2](E3_M2_RefinementAndRecipeExecution.md) |
|
||||
| E3.M1 | Ready | **NEO-41 landed (prototype, superseded on interact by NEO-63):** inline XP on **`resource_node`** interact. **NEO-57–NEO-61** — content, registry, HTTP defs, depletion store (see prior alignment). **NEO-62 landed:** **`GatherOperations`** + **`GatherResult`**. **NEO-63 landed:** **`POST …/interact`** **`resource_node`** → gather engine; **four** **`PrototypeInteractableRegistry`** anchors; Bruno gather → inventory + depletion deny ([NEO-63](../../plans/NEO-63-implementation-plan.md), [`NEO-63` manual QA](../../manual-qa/NEO-63.md)); [server README — Gather via interact (NEO-63)](../../../server/README.md#gather-via-interact-neo-63). **NEO-64 landed:** comment-only **`resource_gathered`** / **`gather_node_depleted`** hook sites in **`GatherOperations.TryGather`** ([NEO-64](../../plans/NEO-64-implementation-plan.md), [`NEO-64` manual QA](../../manual-qa/NEO-64.md)); Epic 3 Slice 2 backlog **E3M1-01**–**E3M1-08** complete. **NEO-73 landed:** client gather feedback — **`skill_progression_client.gd`**, **`prototype_interactable_picker.gd`** (nearest in-range **R**), **`GatherFeedbackLabel`** + **`SkillProgressionLabel`**, auto inventory/skill refresh after successful gather ([NEO-73](../../plans/NEO-73-implementation-plan.md), [`NEO-73` manual QA](../../manual-qa/NEO-73.md)); `client/README.md` gather section. **NEO-75 landed:** capstone gather→refine→make loop in Godot — [`NEO-75` manual QA](../../manual-qa/NEO-75.md), `client/README.md` end-to-end section; Epic 3 Slice 5 client complete. | [NEO-41](../../plans/NEO-41-implementation-plan.md) … [NEO-64](../../plans/NEO-64-implementation-plan.md), [NEO-73](../../plans/NEO-73-implementation-plan.md), [NEO-75](../../plans/NEO-75-implementation-plan.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3_M1](E3_M1_ResourceNodeAndGatherLoop.md); `server/NeonSprawl.Server/Game/Gathering/`, `Game/Interaction/`, `client/scripts/skill_progression_client.gd`, `client/scripts/prototype_interactable_picker.gd` |
|
||||
| E3.M3 | In Progress | **NEO-50 landed:** frozen prototype six-item catalog in [`content/items/prototype_items.json`](../../../content/items/prototype_items.json); [`item-def.schema.json`](../../../content/schemas/item-def.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py). **NEO-51 landed:** fail-fast server load of `content/items/*_items.json` at startup — `server/NeonSprawl.Server/Game/Items/` ([NEO-51](../../plans/NEO-51-implementation-plan.md)); [server README — Item catalog](../../../server/README.md#item-catalog-contentitems-neo-51). **NEO-52 landed:** injectable **`IItemDefinitionRegistry`** + lookup tests ([NEO-52](../../plans/NEO-52-implementation-plan.md)). **NEO-53 landed:** **`GET /game/world/item-definitions`** — `ItemDefinitionsWorldApi` + DTOs in `Game/Items/` ([NEO-53](../../plans/NEO-53-implementation-plan.md), [`NEO-53` manual QA](../../manual-qa/NEO-53.md)); [server README — Item definitions (NEO-53)](../../../server/README.md#item-definitions-neo-53); Bruno `bruno/neon-sprawl-server/item-definitions/`. **NEO-54 landed:** **`IPlayerInventoryStore`** + **`PlayerInventoryOperations`** — 24 bag + 1 equipment slots, stack rules, `V005` migration ([NEO-54](../../plans/NEO-54-implementation-plan.md)). **NEO-55 landed:** **`GET`/`POST /game/players/{id}/inventory`** — `PlayerInventoryApi` + DTOs in `Game/Items/` ([NEO-55](../../plans/NEO-55-implementation-plan.md)); [server README — Player inventory (NEO-54 store, NEO-55 HTTP)](../../../server/README.md#player-inventory-neo-54-store-neo-55-http); Bruno `bruno/neon-sprawl-server/inventory/`. **NEO-56 landed:** comment-only **`item_created`** / **`inventory_transfer_denied`** telemetry hook sites in [`PlayerInventoryOperations`](../../../server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs) ([NEO-56](../../plans/NEO-56-implementation-plan.md), [`NEO-56` manual QA](../../manual-qa/NEO-56.md)); no E9.M1 ingest. **NEO-72 landed:** client inventory HUD — **`inventory_client.gd`**, **`item_definitions_client.gd`**, **`InventoryLabel`**, boot hydrate + **`I`** refresh ([NEO-72](../../plans/NEO-72-implementation-plan.md), [`NEO-72` manual QA](../../manual-qa/NEO-72.md)); `client/README.md` inventory HUD section. **NEO-75 landed:** **`prototype_economy_hud_section.gd`** collapse toggle + capstone manual QA ([NEO-75](../../plans/NEO-75-implementation-plan.md), [`NEO-75` manual QA](../../manual-qa/NEO-75.md)); Epic 3 Slice 5 client complete. | [NEO-50](../../plans/NEO-50-implementation-plan.md), [NEO-51](../../plans/NEO-51-implementation-plan.md), [NEO-52](../../plans/NEO-52-implementation-plan.md), [NEO-53](../../plans/NEO-53-implementation-plan.md), [NEO-54](../../plans/NEO-54-implementation-plan.md), [NEO-55](../../plans/NEO-55-implementation-plan.md), [NEO-56](../../plans/NEO-56-implementation-plan.md), [NEO-72](../../plans/NEO-72-implementation-plan.md), [NEO-75](../../plans/NEO-75-implementation-plan.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3M3-prototype-backlog](../../plans/E3M3-prototype-backlog.md), [E3_M3](E3_M3_ItemizationAndInventorySchema.md) |
|
||||
| E3.M2 | Ready | **NEO-65 landed:** frozen prototype eight-recipe catalog in [`content/recipes/prototype_recipes.json`](../../../content/recipes/prototype_recipes.json); [`recipe-def.schema.json`](../../../content/schemas/recipe-def.schema.json) + [`recipe-io-row.schema.json`](../../../content/schemas/recipe-io-row.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) Slice 3 gates ([NEO-65](../../plans/NEO-65-implementation-plan.md)). **NEO-66 landed:** fail-fast server load of `content/recipes/*_recipes.json` at startup — `server/NeonSprawl.Server/Game/Crafting/` ([NEO-66](../../plans/NEO-66-implementation-plan.md)); [server README — Recipe catalog](../../../server/README.md#recipe-catalog-contentrecipes-neo-66). **NEO-67 landed:** injectable **`IRecipeDefinitionRegistry`** + lookup tests ([NEO-67](../../plans/NEO-67-implementation-plan.md)). **NEO-68 landed:** **`GET /game/world/recipe-definitions`** — `RecipeDefinitionsWorldApi` + DTOs in `Game/Crafting/` ([NEO-68](../../plans/NEO-68-implementation-plan.md), [`NEO-68` manual QA](../../manual-qa/NEO-68.md)); [server README — Recipe definitions (NEO-68)](../../../server/README.md#recipe-definitions-neo-68); Bruno `bruno/neon-sprawl-server/recipe-definitions/`. **NEO-69 landed:** **`CraftOperations.TryCraft`** + **`CraftResult`** — pre-flight inputs/outputs, inventory mutation, refine XP ([NEO-69](../../plans/NEO-69-implementation-plan.md)); [server README — Craft engine (NEO-69)](../../../server/README.md#craft-engine-neo-69-and-craft-http-neo-70). **NEO-70 landed:** **`POST /game/players/{id}/craft`** — `PlayerCraftApi` + wire **`CraftResponse`**; Bruno gather→refine→make spine ([NEO-70](../../plans/NEO-70-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/craft/`. **NEO-71 landed:** comment-only **`item_crafted`** / **`craft_failed`** telemetry hook sites in **`CraftOperations.TryCraft`** ([NEO-71](../../plans/NEO-71-implementation-plan.md)); [server README — Craft telemetry hooks (NEO-71)](../../../server/README.md#craft-telemetry-hooks-neo-71). **NEO-42 landed (prep):** **`RefineActivitySkillXpGrant`** / **`RefineSkillXpConstants`** — wired from craft engine success path. Epic 3 Slice 3 server backlog **E3M2-01**–**E3M2-07** complete. Upstream: E3.M1 **Ready**, E3.M3 inventory landed. **NEO-74 landed:** client craft UI — **`recipe_definitions_client.gd`**, **`craft_client.gd`**, **`craft_recipe_panel.gd`**, **`CraftFeedbackLabel`**, **refine** skill row ([NEO-74](../../plans/NEO-74-implementation-plan.md), [`NEO-74` manual QA](../../manual-qa/NEO-74.md)); `client/README.md` craft section. **NEO-75 landed:** capstone playable gather→refine→make loop — [`NEO-75` manual QA](../../manual-qa/NEO-75.md), **`prototype_economy_hud_section.gd`**; Epic 3 Slice 5 client complete. | [NEO-42](../../plans/NEO-42-implementation-plan.md), [NEO-65](../../plans/NEO-65-implementation-plan.md), [NEO-66](../../plans/NEO-66-implementation-plan.md), [NEO-67](../../plans/NEO-67-implementation-plan.md), [NEO-68](../../plans/NEO-68-implementation-plan.md), [NEO-69](../../plans/NEO-69-implementation-plan.md), [NEO-70](../../plans/NEO-70-implementation-plan.md), [NEO-71](../../plans/NEO-71-implementation-plan.md), [NEO-74](../../plans/NEO-74-implementation-plan.md), [NEO-75](../../plans/NEO-75-implementation-plan.md), [E3M2-prototype-backlog](../../plans/E3M2-prototype-backlog.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3_M2](E3_M2_RefinementAndRecipeExecution.md) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
# NEO-75 — Manual QA checklist
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Key | NEO-75 |
|
||||
| Title | E3: Playable gather→refine→make loop in client (E3S5-04) |
|
||||
| Linear | https://linear.app/neon-sprawl/issue/NEO-75/e3-playable-gatherrefinemake-loop-in-client-e3s5-04 |
|
||||
| Plan | `docs/plans/NEO-75-implementation-plan.md` |
|
||||
| Branch | `NEO-75-e3-playable-gather-refine-make-loop` |
|
||||
|
||||
## Preconditions
|
||||
|
||||
- **Fresh dev player:** stop any running server, then start a new instance so in-memory **`dev-local-1`** inventory, skills, and resource-node depletion reset.
|
||||
- **No Bruno/curl seeding** for this checklist — all materials come from in-client gather (**R**).
|
||||
- NEO-72 inventory HUD, NEO-73 gather feedback, NEO-74 craft UI landed on the same branch or `main`.
|
||||
|
||||
## Material math
|
||||
|
||||
| Step | Action | Scrap | Plates | Result |
|
||||
|------|--------|-------|--------|--------|
|
||||
| Gather ×4 | **R** at four anchors (below) | **+11** | — | 11 scrap |
|
||||
| Refine #1 | Craft **`refine_scrap_standard`** | −5 | +1 | 6 scrap, 1 plate |
|
||||
| Refine #2 | Craft **`refine_scrap_standard`** | −5 | +1 | 1 scrap, 2 plates |
|
||||
| Make | Craft **`make_field_stim_mk0`** | −1 | −2 | **`field_stim_mk0` ×1** |
|
||||
|
||||
## Checklist
|
||||
|
||||
1. Start server: `cd server/NeonSprawl.Server && dotnet run`.
|
||||
2. Run Godot main scene (**F5**). Confirm **`Economy HUD`** toggle is **on**; **`InventoryLabel`** shows empty bag; **`SkillProgressionLabel`** shows **`salvage`** and **`refine`** rows.
|
||||
3. **WASD** to **`prototype_resource_node_alpha`** anchor **(12, −6)**. Press **R** once. **`GatherFeedbackLabel`** shows **`+1`** scrap delta; inventory scrap updates without **I**; **`salvage`** xp increases.
|
||||
4. Walk to **`prototype_subsurface_vein_beta`** **(18, −6)**; press **R** (**+2** scrap). Repeat at **`prototype_bio_mat_gamma`** **(12, 0)** (**+3**) and **`prototype_urban_bulk_delta`** **(18, 0)** (**+5**). Inventory shows **11** scrap total.
|
||||
5. In the recipe panel, click **Craft** on **`refine_scrap_standard`**. **`CraftFeedbackLabel`** shows success; inventory shows **6** scrap and **1** **`refined_plate_stock`**; **`refine`** xp increases.
|
||||
6. Click **Craft** on **`refine_scrap_standard`** again. Inventory shows **1** scrap and **2** plates.
|
||||
7. Click **Craft** on **`make_field_stim_mk0`**. Success feedback shows **`field_stim_mk0`** output; **`InventoryLabel`** lists **`Field Stim Mk0`** (or agreed display name) with quantity **≥ 1**; **`refine`** xp increases again.
|
||||
8. Uncheck **`Economy HUD`** — inventory, skills, and recipe panel hide; gather/craft feedback labels above remain visible. Check **`Economy HUD`** again — panel repopulates with current data.
|
||||
|
||||
## Notes
|
||||
|
||||
- **R** posts the **nearest in-range** `resource_node`; walk to each anchor before pressing **R** (see [NEO-73 manual QA](NEO-73.md) for anchor coords).
|
||||
- Re-run this checklist after a **server restart** (nodes deplete; inventory persists only in memory).
|
||||
- Combat **use** of **`field_stim_mk0`** is out of scope (E5 follow-up).
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Steps 1–8 completable in one session without Bruno/curl.
|
||||
- [ ] **`field_stim_mk0`** quantity **≥ 1** in inventory at step 7.
|
||||
- [ ] **Salvage** xp moved after gather; **refine** xp moved after each successful craft.
|
||||
|
|
@ -150,9 +150,10 @@ Working backlog for **Epic 3 — Slice 5** ([client economy integration](../deco
|
|||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Manual QA checklist completable by a human in one session with server + client running.
|
||||
- [ ] **`field_stim_mk0`** quantity ≥ 1 in inventory at end of script.
|
||||
- [ ] Epic 3 Slice 3 acceptance re-read: **player** (not Bruno) completes gather → refine → usable item.
|
||||
- [x] Manual QA checklist completable by a human in one session with server + client running.
|
||||
- [x] **`field_stim_mk0`** quantity ≥ 1 in inventory at end of script.
|
||||
- [x] Epic 3 Slice 3 acceptance re-read: **player** (not Bruno) completes gather → refine → usable item.
|
||||
- [x] Economy HUD collapse toggle functional ([`NEO-75` manual QA](../manual-qa/NEO-75.md) step 8).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
# NEO-75 — Implementation plan
|
||||
|
||||
## Story reference
|
||||
|
||||
| Field | Value |
|
||||
|--------|--------|
|
||||
| **Key** | NEO-75 |
|
||||
| **Title** | E3: Playable gather→refine→make loop in client (E3S5-04) |
|
||||
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-75/e3-playable-gatherrefinemake-loop-in-client-e3s5-04 |
|
||||
| **Module** | Epic 3 **Slice 5 capstone** — [E3.M1](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) + [E3.M2](../decomposition/modules/E3_M2_RefinementAndRecipeExecution.md) + [E3.M3](../decomposition/modules/E3_M3_ItemizationAndInventorySchema.md) · backlog **E3S5-04** |
|
||||
| **Branch** | `NEO-75-e3-playable-gather-refine-make-loop` |
|
||||
| **Server deps** | NEO-63 gather interact, NEO-70 craft POST, NEO-55 inventory GET, NEO-37 skill-progression GET (**Done** on `main`) |
|
||||
| **Client deps** | [NEO-72](https://linear.app/neon-sprawl/issue/NEO-72) inventory HUD (**Done**); [NEO-73](https://linear.app/neon-sprawl/issue/NEO-73) gather feedback (**Done**); [NEO-74](https://linear.app/neon-sprawl/issue/NEO-74) craft UI (**Done**) |
|
||||
| **Pattern** | Capstone integration — reuse NEO-72–74 wiring; docs + manual QA primary; HUD collapse per kickoff |
|
||||
|
||||
## Kickoff clarifications
|
||||
|
||||
| Topic | Question | Agent recommendation | Answer |
|
||||
|--------|----------|----------------------|--------|
|
||||
| **Implementation scope** | Docs-only vs code changes? | **Docs + manual QA first**; add code only if QA fails — loop already wired in NEO-72–74. | **User:** docs + manual QA first; code only if QA fails. |
|
||||
| **HUD collapse** | Build toggle now or defer? | **Defer** — optional per backlog; left scroll usable for capstone. | **User:** **implement** collapsible HUD sections in this story. |
|
||||
| **Capstone session start** | Fresh player without Bruno? | **Restart server** (in-memory dev store resets) before Godot **F5** — no curl/Bruno seeding. | **Adopted** (Linear AC + kickoff default). |
|
||||
| **Progression gate smoke** | Filter recipes by skill level? | **Visible XP only** — salvage/refine rows refresh after gather/craft; no recipe gating UI (explicit NEO-74 out-of-scope). | **Adopted** (decomposition default). |
|
||||
|
||||
## Goal, scope, and out-of-scope
|
||||
|
||||
**Goal:** Prove Epic 3 prototype acceptance **in Godot**: gather **`scrap_metal_bulk`** → **`refine_scrap_standard`** → **`make_field_stim_mk0`** without Bruno; satisfies vision **crafting loop** + **progression gate** smoke test.
|
||||
|
||||
**In scope (from Linear + [E3S5-04](E3S5-client-prototype-backlog.md#e3s5-04--playable-gatherrefinemake-loop-in-client-capstone)):**
|
||||
|
||||
- **`docs/manual-qa/NEO-75.md`**: numbered **single-session** capstone script — fresh dev player, four-node gather route, two refines + one make, final **`field_stim_mk0`** assertion; **no Bruno/curl** steps.
|
||||
- **`client/README.md`**: **End-to-end economy loop** section — integration checklist (boot → **R** gather → craft panel → inventory/skill refresh chain); input keys; material math (11 scrap minimum).
|
||||
- **Collapsible economy HUD**: toggle to hide/show **`InventoryLabel`**, **`SkillProgressionLabel`**, **`CraftRecipePanel`** block so prototype scene stays playable on smaller viewports.
|
||||
- **Module alignment** (on story completion): update [`documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) E3.M1/M2/M3 rows + Epic 3 Slice 5 “complete” note in [`epic_03_crafting_economy.md`](../decomposition/epics/epic_03_crafting_economy.md).
|
||||
- **Integration fixes only if capstone QA fails** on current `main` wiring (unexpected deny, refresh gap, etc.).
|
||||
|
||||
**Out of scope (from Linear):**
|
||||
|
||||
- Quest UI (E7); combat consumable **use** of **`field_stim_mk0`** (E5 follow-up); art pass.
|
||||
- New HTTP routes or economy clients; recipe quantity spinner; station placement.
|
||||
- Bruno-only verification as prototype-complete proof.
|
||||
|
||||
## Acceptance criteria checklist
|
||||
|
||||
- [x] Manual QA checklist completable by a human in **one session** with server + client running (**`docs/manual-qa/NEO-75.md`**).
|
||||
- [x] **`field_stim_mk0`** quantity ≥ **1** in inventory at end of script.
|
||||
- [x] Epic 3 Slice 3 AC re-read satisfied in Godot: player (not Bruno) completes **gather → refine → usable item**; **salvage** / **refine** XP visible after actions.
|
||||
- [x] Economy HUD collapse toggle documented and functional.
|
||||
|
||||
## Implementation reconciliation (shipped)
|
||||
|
||||
- **HUD:** `prototype_economy_hud_section.gd` — **`Economy HUD`** checkbox collapses inventory/skills/recipe panel under **`UICanvas/EconomyHudSection/Body/`**.
|
||||
- **Docs:** [`NEO-75` manual QA](../manual-qa/NEO-75.md) capstone script (zero Bruno); `client/README.md` end-to-end economy loop section.
|
||||
- **Tests:** `prototype_economy_hud_section_test.gd` — toggle visibility.
|
||||
- **Alignment:** E3.M1/M2/M3 rows + E3S5-04 backlog AC updated for Slice 5 client complete.
|
||||
- **Integration:** No `main.gd` logic changes beyond `@onready` path updates for reparented HUD nodes.
|
||||
|
||||
## Technical approach
|
||||
|
||||
### 1. Capstone manual QA script (`docs/manual-qa/NEO-75.md`)
|
||||
|
||||
**Preconditions:** Stop any running server; start fresh (`dotnet run`) so in-memory **`dev-local-1`** inventory/skills/nodes reset. Godot **F5** with no prior session curl seeding.
|
||||
|
||||
**Material math** (from [`content/recipes/prototype_recipes.json`](../../content/recipes/prototype_recipes.json)):
|
||||
|
||||
| Step | Action | Scrap consumed | Plates gained | Scrap remaining |
|
||||
|------|--------|----------------|---------------|-----------------|
|
||||
| Gather ×4 | **R** at alpha (+1), beta (+2), gamma (+3), delta (+5) | — | — | **11** |
|
||||
| Refine #1 | Craft **`refine_scrap_standard`** | 5 | +1 | **6** |
|
||||
| Refine #2 | Craft **`refine_scrap_standard`** | 5 | +1 | **1** (+ **2** plates) |
|
||||
| Make | Craft **`make_field_stim_mk0`** | 1 | −2 plates | **0** (+ **`field_stim_mk0` ×1**) |
|
||||
|
||||
**Checklist outline:**
|
||||
|
||||
1. Start server + client; confirm empty bag and skill rows on HUD.
|
||||
2. Walk each resource anchor (coords from [NEO-73 manual QA](../manual-qa/NEO-73.md)); **R** once per node — verify **`GatherFeedbackLabel`**, inventory scrap, **salvage** XP without **I**.
|
||||
3. Craft **`refine_scrap_standard`** twice; verify **`CraftFeedbackLabel`**, **refine** XP, plate stock.
|
||||
4. Craft **`make_field_stim_mk0`**; verify consumable in **`InventoryLabel`**.
|
||||
5. Optional: collapse economy HUD, expand again — panel still populated.
|
||||
|
||||
### 2. HUD collapse (`prototype_economy_hud_section.gd`)
|
||||
|
||||
- Add **`UICanvas/EconomyHudSection`** (`VBoxContainer` + script):
|
||||
- Header row: **`CheckButton`** text **`Economy HUD`** (default **pressed** / expanded).
|
||||
- Body **`VBoxContainer`**: reparent existing **`InventoryLabel`**, **`SkillProgressionLabel`**, **`CraftRecipePanel`** from flat **`UICanvas`** (remove fixed **`offset_top`** stacking — use container layout).
|
||||
- **`toggled(pressed)`** → set body **`visible = pressed`**; header stays visible.
|
||||
- **`main.gd`**: no signal rewiring — `@onready` paths update to new hierarchy (or export node refs unchanged if reparent preserves names).
|
||||
- Document toggle in **`client/README.md`** and capstone manual QA step.
|
||||
|
||||
**Keep expanded by default** so capstone QA matches NEO-72–74 expectations; collapse is optional UX for viewport space.
|
||||
|
||||
### 3. README integration checklist
|
||||
|
||||
New **`## End-to-end economy loop (NEO-75)`** section after NEO-74 craft subsection:
|
||||
|
||||
- Flow diagram in prose: boot GETs → **R** nearest resource node → gather feedback + inventory/salvage refresh → scroll craft panel → **Craft** → craft feedback + inventory/refine refresh.
|
||||
- Cross-links: NEO-72 (**I**), NEO-73 (**R** + anchors), NEO-74 (recipes).
|
||||
- Pointer to **`docs/manual-qa/NEO-75.md`** as authoritative capstone script.
|
||||
|
||||
### 4. Module alignment (implementation batch or story end)
|
||||
|
||||
When acceptance criteria pass:
|
||||
|
||||
- **`documentation_and_implementation_alignment.md`**: E3.M1/M2/M3 — note **NEO-75 landed**, Epic 3 Slice 5 client capstone complete.
|
||||
- **`epic_03_crafting_economy.md`**: Slice 5 AC checkboxes / status if not already marked.
|
||||
|
||||
### 5. Integration fixes (conditional)
|
||||
|
||||
Run capstone QA on **`main`** wiring before collapse refactor; if any step fails, fix minimal **`main.gd`** / client bug in this branch and note in plan **Decisions**. Expected path: **no logic changes** beyond HUD layout.
|
||||
|
||||
## Files to add
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `docs/manual-qa/NEO-75.md` | Capstone single-session manual QA (zero Bruno). |
|
||||
| `client/scripts/prototype_economy_hud_section.gd` | Collapse toggle + body visibility for economy HUD block. |
|
||||
| `client/scripts/prototype_economy_hud_section.gd.uid` | Godot UID companion (same commit as `.gd`). |
|
||||
| `client/test/prototype_economy_hud_section_test.gd` | GdUnit: toggle hides/shows body container. |
|
||||
|
||||
## Files to modify
|
||||
|
||||
| Path | Rationale |
|
||||
|------|-----------|
|
||||
| `client/scenes/main.tscn` | Add **`EconomyHudSection`**; reparent inventory/skills/craft panel under collapsible body. |
|
||||
| `client/scripts/main.gd` | Update `@onready` node paths if hierarchy changes; wire collapse only if script needs refs from root. |
|
||||
| `client/README.md` | End-to-end economy loop section + economy HUD toggle note. |
|
||||
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | Record NEO-75 / Slice 5 capstone complete when shipped. |
|
||||
| `docs/decomposition/epics/epic_03_crafting_economy.md` | Slice 5 prototype client completion note (if needed). |
|
||||
|
||||
## Tests
|
||||
|
||||
| File | Coverage |
|
||||
|------|----------|
|
||||
| **`client/test/prototype_economy_hud_section_test.gd`** (add) | **`# Arrange`**: scene with section + body child. **`# Act`**: toggle **`CheckButton`**. **`# Assert`**: body **`visible`** matches pressed state. |
|
||||
| Existing NEO-72–74 client tests | No change expected — capstone reuses their clients; run suite in CI. |
|
||||
|
||||
**No `main.gd` automated test** — orchestration verified via capstone manual QA (same policy as NEO-72–74).
|
||||
|
||||
## Open questions / risks
|
||||
|
||||
| Question / risk | Agent recommendation | Status |
|
||||
|-----------------|---------------------|--------|
|
||||
| **Loop already works on `main`** | Run capstone QA before collapse refactor; expect pass. | **adopted** — no integration logic changes |
|
||||
| **HUD reparent breaks `@onready` paths** | Update paths in **`main.gd`** + **`craft_recipe_panel.gd`** exports in same commit as **`main.tscn`**. | **adopted** |
|
||||
| **Depleted nodes block repeat QA** | Capstone script uses **one R per node** (11 scrap); delta node has capacity 10 — single pass sufficient. Re-run requires **server restart**. | **adopted** |
|
||||
| **Vertical layout shift after collapse container** | Use **`VBoxContainer`** layout instead of absolute **`offset_top`** for economy block only; combat/debug labels above unchanged. | **adopted** |
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# Code review — NEO-75 playable gather→refine→make loop (capstone)
|
||||
|
||||
**Date:** 2026-05-24
|
||||
**Scope:** Branch `NEO-75-e3-playable-gather-refine-make-loop` · commits `fbae314`–`977bcc3` vs `origin/main`
|
||||
**Base:** `origin/main`
|
||||
|
||||
## Verdict
|
||||
|
||||
**Approve with nits** — capstone docs, HUD collapse, and path updates match the adopted plan; GdUnit passes. Merge after human capstone manual QA; minor module/README doc consistency nits below.
|
||||
|
||||
## Summary
|
||||
|
||||
The branch completes Epic 3 Slice 5 capstone integration: collapsible **`EconomyHudSection`** wraps inventory/skills/recipe panel under a **`Economy HUD`** checkbox, **`main.gd`** `@onready` paths follow the reparent, and a focused **`prototype_economy_hud_section.gd`** script toggles body visibility. Docs deliver the zero-Bruno capstone script (`docs/manual-qa/NEO-75.md`), README end-to-end loop section, implementation plan reconciliation, alignment register updates, and E3S5-04 backlog checkboxes. No new HTTP routes or economy client logic — the gather→refine→make loop reuses NEO-72–74 wiring as intended. Risk is low: layout-only client change plus documentation; automated coverage is the HUD toggle suite only (orchestration remains manual QA per plan).
|
||||
|
||||
## Documentation checked
|
||||
|
||||
| Document | Result |
|
||||
|----------|--------|
|
||||
| [`docs/plans/NEO-75-implementation-plan.md`](../plans/NEO-75-implementation-plan.md) | **Matches** — kickoff decisions (docs-first, implement HUD collapse, fresh server session, visible XP only) reflected; acceptance checklist and reconciliation complete. |
|
||||
| [`docs/plans/E3S5-client-prototype-backlog.md`](../plans/E3S5-client-prototype-backlog.md) · **E3S5-04** | **Matches** — capstone manual QA, README integration, optional HUD collapse, alignment cross-links; AC checked. |
|
||||
| [`docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md`](../decomposition/modules/E3_M2_RefinementAndRecipeExecution.md) | **Matches** — status notes NEO-74 + NEO-75 landed. |
|
||||
| [`docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md`](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) | **Matches** — Status row notes NEO-73 gather feedback + NEO-75 capstone landed. |
|
||||
| [`docs/decomposition/modules/E3_M3_ItemizationAndInventorySchema.md`](../decomposition/modules/E3_M3_ItemizationAndInventorySchema.md) | **Matches** — Status row notes NEO-72 inventory HUD + NEO-75 economy collapse landed. |
|
||||
| [`docs/decomposition/modules/documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) | **Matches** — E3.M1/M2/M3 rows note NEO-75 landed and Slice 5 client complete. |
|
||||
| [`docs/decomposition/epics/epic_03_crafting_economy.md`](../decomposition/epics/epic_03_crafting_economy.md) | **Matches** — Slice 5 **Status** note records NEO-75 capstone landed. |
|
||||
| [`docs/decomposition/modules/client_server_authority.md`](../decomposition/modules/client_server_authority.md) | **Matches** — no client-side stack math; display-only HUD collapse. |
|
||||
| [`docs/manual-qa/NEO-75.md`](../manual-qa/NEO-75.md) | **Matches** — single-session script, material math, collapse step; acceptance boxes left for human sign-off (expected). |
|
||||
| [`client/README.md`](../../client/README.md) | **Matches** — NEO-75 loop section; NEO-72/73/74 HUD paths use **`EconomyHudSection/Body/`** hierarchy. |
|
||||
| Full-stack epic decomposition | **Matches** — E3S5-04 **client** capstone completes NEO-72→75 chain; Godot manual QA authoritative (not Bruno-only). |
|
||||
|
||||
## Blocking issues
|
||||
|
||||
(none)
|
||||
|
||||
## Suggestions
|
||||
|
||||
1. ~~**Add Slice 5 capstone note to `epic_03_crafting_economy.md`** — Plan §4 called for a Slice 5 completion note when AC pass; alignment register and backlog are updated but the epic page still reads as open-ended. A one-line “Slice 5 client capstone (NEO-75) landed” under Slice 5 would close the loop.~~ Done.
|
||||
|
||||
2. ~~**Refresh E3.M1 / E3.M3 module Status rows** — E3.M2 was updated; E3.M1 and E3.M3 module pages still show pre–Slice 5 status while the alignment register already cites NEO-73/72/75. Mirror the E3.M2 pattern (client slice landed + capstone) for doc parity.~~ Done.
|
||||
|
||||
3. ~~**Align NEO-73 README HUD paths** — Update the gather section to `UICanvas/EconomyHudSection/Body/InventoryLabel` and `…/SkillProgressionLabel` so all three Slice 5 README sections reference the same hierarchy after the reparent.~~ Done (NEO-74 craft HUD paths aligned in the same pass).
|
||||
|
||||
## Nits
|
||||
|
||||
- Nit: **`test_toggle_collapses_body`** drives collapse via direct **`button_pressed = false`** rather than simulating a user click on **`ToggleButton`** — acceptable in headless GdUnit; behavior is covered.
|
||||
- Nit: **`set_expanded`** and **`_on_toggle_toggled`** duplicate visibility logic — fine at this size; only matters if collapse rules grow (e.g. persist preference).
|
||||
- ~~Nit: Capstone AC for HUD collapse is checked in the implementation plan but not listed as a separate bullet in E3S5-04 backlog acceptance criteria — optional backlog tweak for traceability.~~ Done — E3S5-04 AC bullet added.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# NEO-75 GdUnit suite (from client/)
|
||||
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode \
|
||||
-a res://test/prototype_economy_hud_section_test.gd
|
||||
|
||||
# Full client suite (CI parity)
|
||||
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode
|
||||
```
|
||||
|
||||
Manual (required before claiming capstone complete): follow [`docs/manual-qa/NEO-75.md`](../manual-qa/NEO-75.md) — **fresh server restart**, four-node gather (**11** scrap), two **`refine_scrap_standard`**, one **`make_field_stim_mk0`**, verify **`field_stim_mk0` ≥ 1**, salvage/refine XP movement, and economy HUD collapse/expand with populated panel.
|
||||
Loading…
Reference in New Issue