neon-sprawl/docs/plans/NEO-68-implementation-plan.md

8.8 KiB

NEO-68 — Implementation plan

Story reference

Field Value
Key NEO-68
Title E3.M2: GET world recipe-definitions + Bruno
Linear https://linear.app/neon-sprawl/issue/NEO-68/e3m2-get-world-recipe-definitions-bruno
Module E3.M2 — RefinementAndRecipeExecution · Epic 3 Slice 3 · backlog E3M2-04
Branch NEO-68-get-world-recipe-definitions-bruno
Precursor NEO-67IRecipeDefinitionRegistry + DI (Done on main)
Pattern NEO-53GET /game/world/item-definitions
Client counterpart NEO-74 — E3S5-03 craft UI + recipe list (blocked by this issue; server-only in this story)
Blocks NEO-74, NEO-73 (Linear relations)

Kickoff clarifications

Topic Question Agent recommendation Answer
JSON row fields Expose required six + I/O only vs optional nullables? Required six + inputs/outputs only — prototype rows omit requiredSkillLevel / stationTag; mirror NEO-53 “loaded fields only”. User: required six + I/O only.
Response envelope Array property name? recipes (with schemaVersion 1) — parallel to NEO-36 skills / NEO-53 items; matches content vocabulary. Adopted — no objection; follows precedent.
Route path /game/world/… vs /game/content/…? GET /game/world/recipe-definitions — E3M2 backlog + parallel to item/skill defs. Adopted — backlog.
Manual QA doc Add docs/manual-qa/NEO-68.md? Yes — player-visible HTTP; NEO-53 pattern. User: yes.

Goal, scope, and out-of-scope

Goal: Expose a stable, read-only JSON endpoint that returns all eight loaded prototype recipe definitions for Bruno, manual QA, client preview (NEO-74), and tooling — backed by IRecipeDefinitionRegistry without duplicating catalog truth.

In scope (from Linear + E3M2-04):

  • GET /game/world/recipe-definitions with versioned envelope (schemaVersion 1, recipes array).
  • Row fields: id, displayName, recipeKind, requiredSkillId, inputs, outputs (I/O rows: itemId, quantity; camelCase JSON).
  • Recipes ordered by id ordinal, matching GetDefinitionsInIdOrder().
  • Bruno folder bruno/neon-sprawl-server/recipe-definitions/.
  • docs/manual-qa/NEO-68.md.
  • server/README.md section.
  • API integration tests (AAA).

Out of scope (from Linear + backlog):

  • Per-player craft history or cooldown state.
  • Optional schema fields (requiredSkillLevel, stationTag) until content ships them.
  • Craft execution (NEO-69).
  • Godot client (NEO-74).

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.

Technical approach

  1. Route: GET /game/world/recipe-definitions — parameterless read; inject IRecipeDefinitionRegistry only (not RecipeDefinitionCatalog).

  2. Response shape: Top-level schemaVersion (RecipeDefinitionsListResponse.CurrentSchemaVersion = 1) plus recipes array. Each row maps from RecipeDefRow: id, displayName, recipeKind, requiredSkillId, inputs, outputs. Do not emit optional null fields on this contract.

  3. I/O projection: Map RecipeIoRow → JSON with itemId, quantity (same keys as content schema).

  4. Ordering: Build list from registry.GetDefinitionsInIdOrder() (already ordinal by id). Tests assert exact id sequence (frozen eight from E3.M2 snapshot):

    make_armor_quick, make_contract_token, make_field_stim_batch, make_field_stim_mk0, make_prototype_armor, make_survey_drone_kit, refine_scrap_efficient, refine_scrap_standard.

  5. Implementation: New RecipeDefinitionsWorldApi + RecipeDefinitionsListDtos.cs in Game/Crafting/ (mirror ItemDefinitionsWorldApi). 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, 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 Related implementation slices and documentation_and_implementation_alignment.md E3.M2 row when landed.

Files to add

Path Purpose
server/NeonSprawl.Server/Game/Crafting/RecipeDefinitionsWorldApi.cs Map* extension registering GET route; maps registry → JSON.
server/NeonSprawl.Server/Game/Crafting/RecipeDefinitionsListDtos.cs Versioned response + row + I/O DTOs for JSON serialization.
server/NeonSprawl.Server.Tests/Game/Crafting/RecipeDefinitionsWorldApiTests.cs HTTP integration: 200, schema v1, eight ids in order, spot-check rows + I/O.
bruno/neon-sprawl-server/recipe-definitions/Get recipe definitions.bru Manual / Bruno verification against dev server.
bruno/neon-sprawl-server/recipe-definitions/folder.bru Bruno folder metadata (match item-definitions).
docs/manual-qa/NEO-68.md Checklist: start server, curl GET, confirm eight ids + spot-check I/O.
docs/plans/NEO-68-implementation-plan.md This plan.

Files to modify

Path Rationale
server/NeonSprawl.Server/Program.cs Register MapRecipeDefinitionsWorldApi() alongside other game world definition APIs.
server/README.md Document GET /game/world/recipe-definitions, curl example, links to plan/manual QA/Bruno.
docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md Related implementation slices — HTTP read model bullet (NEO-68).
docs/decomposition/modules/documentation_and_implementation_alignment.md E3.M2 row — note NEO-68 HTTP projection when landed.

Tests

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 FrozenEightInIdOrder (shared constant referenced by manual QA / Bruno); spot-check refine_scrap_standard and make_field_stim_mk0. AAA per csharp-style.

Bruno scripts complement automated tests for manual dev-server verification.

Open questions / risks

Question / risk Agent recommendation Status
Linear blockedBy NEO-67 Branch from main where NEO-67 is merged (confirmed at kickoff). adopted
Optional fields later When content adds requiredSkillLevel / stationTag, bump schemaVersion or extend v1 with documented null semantics in a follow-up issue. deferred
Client not in this story Cross-link NEO-74 in plan; Bruno + manual QA are not prototype-complete per full-stack epic decomposition. adopted

Decisions (kickoff)

  • Required six + I/O only — no optional null fields on v1 contract (user confirmed).
  • recipes array key — consistent with entity naming in sibling world-definition endpoints.
  • docs/manual-qa/NEO-68.md included (user confirmed).
  • Registry-only injection — HTTP layer depends on IRecipeDefinitionRegistry, not RecipeDefinitionCatalog.