From 529cb616f571684bee3b2a024100276db12aa2e2 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 24 May 2026 17:08:18 -0400 Subject: [PATCH] NEO-68: Update docs and strike through code review findings. Mark E3M2-04 landed, refresh alignment register and README, reconcile plan AC. --- .../modules/E3_M2_RefinementAndRecipeExecution.md | 2 +- .../documentation_and_implementation_alignment.md | 2 +- docs/plans/E3M2-prototype-backlog.md | 6 ++++-- docs/plans/NEO-68-implementation-plan.md | 10 +++++----- docs/reviews/2026-05-24-NEO-68.md | 10 +++++----- server/README.md | 8 ++++++++ 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md b/docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md index 100e260..f0575fd 100644 --- a/docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md +++ b/docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md @@ -13,7 +13,7 @@ **Prep (NEO-42):** Server ships **`RefineActivitySkillXpGrant.GrantOnSuccessfulCraftOrRefine`** + **`RefineSkillXpConstants`** under `server/NeonSprawl.Server/Game/Skills/` — delegates to **`SkillProgressionGrantOperations.TryApplyGrant`** with **`refine`** + **`activity`** (prototype **10** XP), same persistence and **`allowedXpSourceKinds`** rules as **`POST …/skill-progression`**. **No** `CraftRequest` / **`CraftResult`** route yet; when this module adds authoritative craft/refine success, call that helper once on success ([NEO-42 implementation plan](../../plans/NEO-42-implementation-plan.md); [server README — Craft / refine hook (NEO-42)](../../../server/README.md#craft--refine-hook--skill-xp-neo-42)). -**E3M2-01 (NEO-65):** Frozen 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 plan](../../plans/NEO-65-implementation-plan.md)). **E3M2-02 (NEO-66):** fail-fast server load under `server/NeonSprawl.Server/Game/Crafting/` — `RecipeDefinitionCatalogLoader`, `RecipeDefinitionCatalog`, cross-check vs item + skill catalogs, Slice 3 gate; [NEO-66 plan](../../plans/NEO-66-implementation-plan.md); [server README — Recipe catalog](../../../server/README.md#recipe-catalog-contentrecipes-neo-66). **E3M2-03 (NEO-67):** injectable **`IRecipeDefinitionRegistry`** + DI; [NEO-67 plan](../../plans/NEO-67-implementation-plan.md). +**E3M2-01 (NEO-65):** Frozen 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 plan](../../plans/NEO-65-implementation-plan.md)). **E3M2-02 (NEO-66):** fail-fast server load under `server/NeonSprawl.Server/Game/Crafting/` — `RecipeDefinitionCatalogLoader`, `RecipeDefinitionCatalog`, cross-check vs item + skill catalogs, Slice 3 gate; [NEO-66 plan](../../plans/NEO-66-implementation-plan.md); [server README — Recipe catalog](../../../server/README.md#recipe-catalog-contentrecipes-neo-66). **E3M2-03 (NEO-67):** injectable **`IRecipeDefinitionRegistry`** + DI; [NEO-67 plan](../../plans/NEO-67-implementation-plan.md). **E3M2-04 (NEO-68):** **`GET /game/world/recipe-definitions`** — versioned read-only projection (`schemaVersion` **1**, **`recipes`**) backed by **`IRecipeDefinitionRegistry`**; Bruno `bruno/neon-sprawl-server/recipe-definitions/`. Plan: [NEO-68 implementation plan](../../plans/NEO-68-implementation-plan.md). ## Purpose diff --git a/docs/decomposition/modules/documentation_and_implementation_alignment.md b/docs/decomposition/modules/documentation_and_implementation_alignment.md index d0af413..c10c93f 100644 --- a/docs/decomposition/modules/documentation_and_implementation_alignment.md +++ b/docs/decomposition/modules/documentation_and_implementation_alignment.md @@ -55,7 +55,7 @@ Rows appear when work starts; default for unlisted modules is **Planned** / not | 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. **Slice 5 client (planned):** gather feedback HUD [NEO-73](https://linear.app/neon-sprawl/issue/NEO-73) — [E3S5 backlog](../../plans/E3S5-client-prototype-backlog.md). | [NEO-41](../../plans/NEO-41-implementation-plan.md) … [NEO-64](../../plans/NEO-64-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/` | | 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. **Slice 5 client (planned):** inventory HUD [NEO-72](https://linear.app/neon-sprawl/issue/NEO-72) — [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), [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 | In Progress | **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-42 landed (prep):** **`RefineActivitySkillXpGrant`** — craft/refine success should award **`refine`** XP via shared NEO-38 grant path; no craft route yet. **Slice 3 backlog in Linear:** [NEO-68](https://linear.app/neon-sprawl/issue/NEO-68) → [NEO-71](https://linear.app/neon-sprawl/issue/NEO-71); [E3M2-prototype-backlog.md](../../plans/E3M2-prototype-backlog.md) — **E3M2-04**–**E3M2-07** open. Upstream: E3.M1 **Ready**, E3.M3 inventory landed. **Slice 5 client (planned):** craft UI [NEO-74](https://linear.app/neon-sprawl/issue/NEO-74); 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](https://linear.app/neon-sprawl/issue/NEO-67) … [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.M2 | In Progress | **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-42 landed (prep):** **`RefineActivitySkillXpGrant`** — craft/refine success should award **`refine`** XP via shared NEO-38 grant path; no craft route yet. **Slice 3 backlog in Linear:** [NEO-69](https://linear.app/neon-sprawl/issue/NEO-69) → [NEO-71](https://linear.app/neon-sprawl/issue/NEO-71); [E3M2-prototype-backlog.md](../../plans/E3M2-prototype-backlog.md) — **E3M2-05**–**E3M2-07** open. Upstream: E3.M1 **Ready**, E3.M3 inventory landed. **Slice 5 client (planned):** craft UI [NEO-74](https://linear.app/neon-sprawl/issue/NEO-74); 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-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) | --- diff --git a/docs/plans/E3M2-prototype-backlog.md b/docs/plans/E3M2-prototype-backlog.md index dfecd2a..6289120 100644 --- a/docs/plans/E3M2-prototype-backlog.md +++ b/docs/plans/E3M2-prototype-backlog.md @@ -149,8 +149,10 @@ Working backlog for **Epic 3 — Slice 3** ([recipes and crafting pipeline](../d **Acceptance criteria** -- [ ] GET returns all eight prototype recipes with schema version field and input/output summary fields needed for QA. -- [ ] Bruno happy path documented. +- [x] GET returns all eight prototype recipes with schema version field and input/output summary fields needed for QA. +- [x] Bruno happy path documented. + +**Landed ([NEO-68](https://linear.app/neon-sprawl/issue/NEO-68)):** `GET /game/world/recipe-definitions` — `RecipeDefinitionsWorldApi` + DTOs in `server/NeonSprawl.Server/Game/Crafting/`; plan [NEO-68-implementation-plan.md](NEO-68-implementation-plan.md); manual QA [`NEO-68`](../manual-qa/NEO-68.md); Bruno `bruno/neon-sprawl-server/recipe-definitions/`. --- diff --git a/docs/plans/NEO-68-implementation-plan.md b/docs/plans/NEO-68-implementation-plan.md index b183fa1..dae0f14 100644 --- a/docs/plans/NEO-68-implementation-plan.md +++ b/docs/plans/NEO-68-implementation-plan.md @@ -46,9 +46,9 @@ ## Acceptance criteria checklist -- [ ] GET returns all **eight** prototype recipes with **`schemaVersion`** **1** and input/output summary fields needed for QA. -- [ ] Bruno happy path documented and runnable against dev server. -- [ ] Automated API tests (AAA) assert eight ids, id order, and spot-check metadata + I/O rows. +- [x] GET returns all **eight** prototype recipes with **`schemaVersion`** **1** and input/output summary fields needed for QA. +- [x] Bruno happy path documented and runnable against dev server. +- [x] Automated API tests (AAA) assert eight ids, id order, and spot-check metadata + I/O rows. ## Technical approach @@ -64,7 +64,7 @@ 5. **Implementation:** New **`RecipeDefinitionsWorldApi`** + **`RecipeDefinitionsListDtos.cs`** in `Game/Crafting/` (mirror [`ItemDefinitionsWorldApi`](../../server/NeonSprawl.Server/Game/Items/ItemDefinitionsWorldApi.cs)). Wire **`app.MapRecipeDefinitionsWorldApi()`** from **`Program.cs`** next to **`MapItemDefinitionsWorldApi()`**. -6. **Bruno:** `bruno/neon-sprawl-server/recipe-definitions/` with **`Get recipe definitions.bru`** + **`folder.bru`**. Tests: 200, JSON, **`schemaVersion` 1**, **`recipes.length === 8`**, ascending id order, frozen eight ids present, spot-check **`refine_scrap_standard`** (`recipeKind` `process`, inputs/outputs) and **`make_field_stim_mk0`** (`recipeKind` `make`, multi-input). +6. **Bruno:** `bruno/neon-sprawl-server/recipe-definitions/` with **`Get recipe definitions.bru`** + **`folder.bru`**. Tests: 200, JSON, **`schemaVersion` 1**, **`recipes.length === 8`**, ascending id order, **exact frozen-eight registry sequence** (mirror NEO-53 item-definitions Bruno), spot-check **`refine_scrap_standard`** (`recipeKind` `process`, inputs/outputs) and **`make_field_stim_mk0`** (`recipeKind` `make`, multi-input). 7. **Docs:** `docs/manual-qa/NEO-68.md` (mirror NEO-53). Update **`server/README.md`** recipe section with GET + curl. Update [E3_M2](E3_M2_RefinementAndRecipeExecution.md) **Related implementation slices** and [documentation_and_implementation_alignment.md](documentation_and_implementation_alignment.md) E3.M2 row when landed. @@ -93,7 +93,7 @@ | Test file | What it covers | |-----------|----------------| -| `server/NeonSprawl.Server.Tests/Game/Crafting/RecipeDefinitionsWorldApiTests.cs` | **Integration:** `GET /game/world/recipe-definitions` via **`InMemoryWebApplicationFactory`**; **`ReadFromJsonAsync`**; assert **`schemaVersion` 1**; **`recipes`** count **8**; ordered **`id`** list equals frozen eight (ordinal); spot-check **`refine_scrap_standard`** (`displayName`, `recipeKind` `process`, `requiredSkillId` `refine`, input `scrap_metal_bulk` qty 5, output `refined_plate_stock` qty 1) and **`make_field_stim_mk0`** (`recipeKind` `make`, two input rows). **AAA** per [csharp-style](../../.cursor/rules/csharp-style.md). | +| `server/NeonSprawl.Server.Tests/Game/Crafting/RecipeDefinitionsWorldApiTests.cs` | **Integration:** `GET /game/world/recipe-definitions` via **`InMemoryWebApplicationFactory`**; **`ReadFromJsonAsync`**; assert **`schemaVersion` 1**; **`recipes`** count **8**; ordered **`id`** list equals **`FrozenEightInIdOrder`** (shared constant referenced by manual QA / Bruno); spot-check **`refine_scrap_standard`** and **`make_field_stim_mk0`**. **AAA** per [csharp-style](../../.cursor/rules/csharp-style.md). | Bruno scripts complement automated tests for manual dev-server verification. diff --git a/docs/reviews/2026-05-24-NEO-68.md b/docs/reviews/2026-05-24-NEO-68.md index 5ea212c..b2dcb1a 100644 --- a/docs/reviews/2026-05-24-NEO-68.md +++ b/docs/reviews/2026-05-24-NEO-68.md @@ -33,17 +33,17 @@ None (plan-only scope). ## Suggestions -1. **Bruno — exact frozen-eight id order** — Plan section 6 requires ascending order and frozen eight presence. After NEO-53 review, [`Get item definitions.bru`](../../bruno/neon-sprawl-server/item-definitions/Get%20item%20definitions.bru) also asserts the **exact** registry sequence (not just sort + set membership). Mirror that with a `frozen prototype eight matches registry id order` Bruno test using the plan’s frozen list so manual runs catch ordering regressions without `dotnet test`. +1. ~~**Bruno — exact frozen-eight id order** — Plan section 6 requires ascending order and frozen eight presence. After NEO-53 review, [`Get item definitions.bru`](../../bruno/neon-sprawl-server/item-definitions/Get%20item%20definitions.bru) also asserts the **exact** registry sequence (not just sort + set membership). Mirror that with a `frozen prototype eight matches registry id order` Bruno test using the plan’s frozen list so manual runs catch ordering regressions without `dotnet test`.~~ **Done.** Bruno test asserts exact frozen-eight sequence. -2. **Shared frozen id constant** — Follow [`ItemDefinitionsWorldApiTests`](../../server/NeonSprawl.Server.Tests/Game/Items/ItemDefinitionsWorldApiTests.cs): define a `FrozenEightInIdOrder` string array in the integration test file and reference the same sequence in manual QA / Bruno docs to avoid drift from the E3.M2 freeze table. +2. ~~**Shared frozen id constant** — Follow [`ItemDefinitionsWorldApiTests`](../../server/NeonSprawl.Server.Tests/Game/Items/ItemDefinitionsWorldApiTests.cs): define a `FrozenEightInIdOrder` string array in the integration test file and reference the same sequence in manual QA / Bruno docs to avoid drift from the E3.M2 freeze table.~~ **Done.** `RecipeDefinitionsWorldApiTests.FrozenEightInIdOrder`; manual QA and Bruno mirror the sequence. -3. **E3M2 backlog checkboxes on land** — When implementation merges, update **E3M2-04** acceptance checkboxes and landed note in [`E3M2-prototype-backlog.md`](../plans/E3M2-prototype-backlog.md) (same pattern as NEO-66/67). +3. ~~**E3M2 backlog checkboxes on land** — When implementation merges, update **E3M2-04** acceptance checkboxes and landed note in [`E3M2-prototype-backlog.md`](../plans/E3M2-prototype-backlog.md) (same pattern as NEO-66/67).~~ **Done.** ## Nits -- Nit: Plan lists **`RecipeDefinitionsListDtos.cs`** as a separate file — NEO-53 keeps DTOs in one file with the API class; either layout is fine; pick one and stay consistent with the Items folder convention. +- ~~Nit: Plan lists **`RecipeDefinitionsListDtos.cs`** as a separate file — NEO-53 keeps DTOs in one file with the API class; either layout is fine; pick one and stay consistent with the Items folder convention.~~ **Done.** Separate DTO file matches NEO-53 (`ItemDefinitionsListDtos.cs` + `ItemDefinitionsWorldApi.cs`). -- Nit: Manual QA template in plan says “mirror NEO-53” — include the metadata table header (`Key`, `Title`, `Linear`, `Plan`, `Branch`) from [`docs/manual-qa/NEO-53.md`](../manual-qa/NEO-53.md) for consistency. +- ~~Nit: Manual QA template in plan says “mirror NEO-53” — include the metadata table header (`Key`, `Title`, `Linear`, `Plan`, `Branch`) from [`docs/manual-qa/NEO-53.md`](../manual-qa/NEO-53.md) for consistency.~~ **Done.** ## Verification diff --git a/server/README.md b/server/README.md index 554459e..bef322b 100644 --- a/server/README.md +++ b/server/README.md @@ -80,6 +80,14 @@ On startup the host loads every **`*_recipes.json`** under the recipes directory On success, **Information** logs include the resolved recipes directory path, distinct recipe count, and catalog file count. Game code should use **`IRecipeDefinitionRegistry`** for lookups (NEO-67). The catalog singleton remains for fail-fast startup only; do not inject **`RecipeDefinitionCatalog`** in new game code. +## Recipe definitions (NEO-68) + +**`GET /game/world/recipe-definitions`** returns a versioned JSON body (`schemaVersion` **1**, **`recipes`**) backed by **`IRecipeDefinitionRegistry`** — the same prototype rows loaded at startup (no second source of truth). Each row includes **`id`**, **`displayName`**, **`recipeKind`**, **`requiredSkillId`**, and nested **`inputs`** / **`outputs`** (`itemId`, `quantity`). Plan: [NEO-68 implementation plan](../../docs/plans/NEO-68-implementation-plan.md); manual QA: [`docs/manual-qa/NEO-68.md`](../../docs/manual-qa/NEO-68.md); Bruno: `bruno/neon-sprawl-server/recipe-definitions/`. + +```bash +curl -sS -i "http://localhost:5253/game/world/recipe-definitions" +``` + ## Resource node definitions (NEO-60) **`GET /game/world/resource-node-definitions`** returns a versioned JSON body (`schemaVersion` **1**, **`nodes`**) backed by **`IResourceNodeDefinitionRegistry`** — the same prototype rows loaded at startup (no second source of truth). Each row includes **`id`**, **`displayName`**, **`gatherLens`**, **`maxGathers`**, and nested **`yield`** (`itemId`, `quantity`). Plan: [NEO-60 implementation plan](../../docs/plans/NEO-60-implementation-plan.md); manual QA: [`docs/manual-qa/NEO-60.md`](../../docs/manual-qa/NEO-60.md); Bruno: `bruno/neon-sprawl-server/resource-node-definitions/`.