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

8.4 KiB
Raw Permalink Blame History

NEO-60 — Implementation plan

Story reference

Field Value
Key NEO-60
Title E3.M1: GET world resource-node-definitions + Bruno
Linear https://linear.app/neon-sprawl/issue/NEO-60/e3m1-get-world-resource-node-definitions-bruno
Module E3.M1 — ResourceNodeAndGatherLoop · Epic 3 Slice 2 · backlog E3M1-04
Branch NEO-60-e3m1-get-world-resource-node-definitions-bruno
Precursor NEO-59IResourceNodeDefinitionRegistry + DI (Done on main)
Pattern NEO-53GET /game/world/item-definitions

Kickoff clarifications

Topic Question Agent recommendation Answer
Yield projection Nested yield object vs flat itemId/quantity on the row? Nested yield: { itemId, quantity } — mirrors catalog split (node def + yield table) and matches NEO-59 registry shape; room for multi-yield later. User: nested yield object.
Row id JSON name id vs nodeDefId? id — same HTTP convention as item/skill definition endpoints (NEO-53 / NEO-36). User: id.
Manual QA doc Add docs/manual-qa/NEO-60.md? Yes — user-visible HTTP surface; mirror NEO-53. User: yes.

Goal, scope, and out-of-scope

Goal: Expose a stable, read-only JSON endpoint that returns all loaded prototype resource-node definitions (with yield summary) for dev tooling, Bruno, manual QA, and future client/codegen, backed by IResourceNodeDefinitionRegistry without duplicating catalog truth outside the server.

In scope (from Linear + E3M1-04):

  • GET /game/world/resource-node-definitions with versioned envelope (schemaVersion 1, nodes array).
  • Each row: id, displayName, gatherLens, maxGathers, nested yield with itemId and quantity (camelCase JSON).
  • Nodes ordered by id ordinal, matching GetDefinitionsInIdOrder().
  • Bruno folder bruno/neon-sprawl-server/resource-node-definitions/.
  • docs/manual-qa/NEO-60.md.
  • server/README.md section.
  • API integration tests (AAA).

Out of scope (from Linear + backlog):

  • Per-instance remaining capacity / depletion state (NEO-61).
  • Gather engine, inventory mutation, interact wiring (NEO-62+).
  • Client HUD.

Acceptance criteria checklist

  • GET returns all four prototype defs with schemaVersion 1 and yield summary fields needed for QA.
  • Bruno happy path documented and runnable against dev server.
  • Automated API tests (AAA) assert four ids in order and spot-check node + yield metadata.

Technical approach

  1. Route: GET /game/world/resource-node-definitions — parameterless read; inject IResourceNodeDefinitionRegistry only (not ResourceNodeCatalog).

  2. Response shape: Top-level schemaVersion (ResourceNodeDefinitionsListResponse.CurrentSchemaVersion = 1) plus nodes array. Each row maps from ResourceNodeDefRow + joined ResourceYieldRow via TryGetYield: idNodeDefId, displayName, gatherLens, maxGathers, yield{ itemId, quantity }. Prototype catalog guarantees one yield per node at load time; if TryGetYield fails for an enumerated def, treat as programmer error (should not occur post-NEO-58 validation).

  3. Ordering: Build list from registry.GetDefinitionsInIdOrder(). Tests assert exact id sequence (ordinal):

    prototype_bio_mat_gamma, prototype_resource_node_alpha, prototype_subsurface_vein_beta, prototype_urban_bulk_delta.

  4. Implementation: New ResourceNodeDefinitionsWorldApi + ResourceNodeDefinitionsListDtos.cs in Game/Gathering/ (mirror ItemDefinitionsWorldApi). Wire app.MapResourceNodeDefinitionsWorldApi() from Program.cs next to MapItemDefinitionsWorldApi().

  5. Bruno: bruno/neon-sprawl-server/resource-node-definitions/ with Get resource node definitions.bru + folder.bru. Tests: 200, JSON, schemaVersion 1, nodes.length === 4, ascending id order, frozen four ids in registry order, spot-check prototype_resource_node_alpha (gatherLens consumer_salvage, maxGathers 10, yield.itemId scrap_metal_bulk, yield.quantity 1) and prototype_urban_bulk_delta (yield.quantity 5).

  6. Docs: docs/manual-qa/NEO-60.md (mirror NEO-53). Update server/README.md resource-node section with GET + curl. Update E3_M1 Related implementation slices and documentation_and_implementation_alignment.md E3.M1 row when landed. Flip E3M1-04 acceptance checkboxes in E3M1-prototype-backlog.md.

Files to add

Path Purpose
server/NeonSprawl.Server/Game/Gathering/ResourceNodeDefinitionsWorldApi.cs Map* extension registering GET route; maps registry → JSON (node + nested yield).
server/NeonSprawl.Server/Game/Gathering/ResourceNodeDefinitionsListDtos.cs Versioned response + row/yield DTOs for JSON serialization.
server/NeonSprawl.Server.Tests/Game/Gathering/ResourceNodeDefinitionsWorldApiTests.cs HTTP integration: 200, schema v1, four ids in order, spot-check rows + yields.
bruno/neon-sprawl-server/resource-node-definitions/Get resource node definitions.bru Manual / Bruno verification against dev server.
bruno/neon-sprawl-server/resource-node-definitions/folder.bru Bruno folder metadata (match item-definitions).
docs/manual-qa/NEO-60.md Checklist: start server, curl GET, confirm four ids + spot-check yield.

Files to modify

Path Rationale
server/NeonSprawl.Server/Program.cs Register MapResourceNodeDefinitionsWorldApi() alongside other game world APIs.
server/README.md Document GET /game/world/resource-node-definitions, curl example, links to plan/manual QA/Bruno.
docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md Related implementation slices — HTTP read model bullet (NEO-60).
docs/decomposition/modules/documentation_and_implementation_alignment.md E3.M1 row — note NEO-60 HTTP projection when landed.
docs/plans/E3M1-prototype-backlog.md E3M1-04 acceptance checkboxes when landed.

Tests

Test file What it covers
server/NeonSprawl.Server.Tests/Game/Gathering/ResourceNodeDefinitionsWorldApiTests.cs Integration: GET /game/world/resource-node-definitions via InMemoryWebApplicationFactory; ReadFromJsonAsync; assert schemaVersion 1; nodes count 4; ordered id list equals frozen four (ordinal); spot-check prototype_resource_node_alpha (displayName “Prototype Salvage Heap”, gatherLens consumer_salvage, maxGathers 10, yield.itemId scrap_metal_bulk, yield.quantity 1) and prototype_urban_bulk_delta (gatherLens urban_bulk, yield.quantity 5). 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-59 Branch from main where NEO-59 is merged (confirmed at kickoff). adopted
Missing yield at map time Rely on NEO-58 load parity (every node has exactly one yield row); no silent omission in HTTP mapper. adopted
Instance depletion in response Omit remaining gathers — instance state is NEO-61; this endpoint is definition catalog only. adopted

None blocking.

Decisions (kickoff)

  • Nested yield object{ itemId, quantity } per row; joined from registrys separate TryGet methods.
  • Row id JSON name — consistent with item/skill world definition endpoints.
  • nodes top-level array — parallel to items / skills naming on sibling routes.
  • Manual QA doc includeddocs/manual-qa/NEO-60.md.