6.6 KiB
Code review — NEO-70 craft HTTP + Bruno spine
Date: 2026-05-24
Scope: Branch NEO-70-craft-http-bruno-gather-refine-make-spine · commits b148db8–46393b8 vs origin/main
Base: origin/main
Verdict
Approve with nits — implementation matches the adopted plan and E3M2-06 acceptance criteria; ready to merge after optional follow-ups below.
Summary
The branch adds POST /game/players/{id}/craft via PlayerCraftApi and versioned wire DTOs in CraftWireDtos.cs, mapping request → CraftOperations.TryCraft → CraftResponse. HTTP behavior follows NEO-55 precedent: known-player gate, 400 on contract errors, 404 on unknown player or store-missing codes, 200 + structured deny for rule failures. Eight AAA integration tests cover success, major deny paths, and error statuses. Bruno craft/ folder documents the full gather→refine→make spine (11 scrap from four nodes → refine_scrap_efficient → make_field_stim_mk0) plus five deny scenarios. Docs (plan, module snapshot, backlog E3M2-06, alignment table, README) are updated and cross-link NEO-74. Risk is low for prototype scope; gaps are mostly deferred store-missing HTTP tests (explicitly allowed by plan) and minor naming / Bruno isolation nits.
Documentation checked
| Document | Result |
|---|---|
docs/plans/NEO-70-implementation-plan.md |
Matches — CraftResult-only wire response, quantity default 1, HTTP status mapping, Bruno spine math (11 scrap + efficient refine), deny matrix, no manual QA doc (kickoff), NEO-74 cross-link, acceptance checklist complete. |
docs/plans/E3M2-prototype-backlog.md · E3M2-06 |
Matches — acceptance criteria checked; landed note present. Generic bullet “Add manual QA when craft HTTP lands” remains (NEO-70 opted out per kickoff — see Nits). |
docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md |
Matches — implementation snapshot includes NEO-70 craft HTTP + wire CraftResponse field table. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
Matches — E3.M2 row notes NEO-70 landed; Bruno path cited. |
docs/decomposition/modules/client_server_authority.md |
Matches — server-authoritative craft mutation; client deferred to NEO-74. |
NEO-55 reference — PlayerInventoryApi |
Matches — same position gate, schemaVersion 400, store-missing → 404, rule deny → 200 + structured body. |
| Full-stack epic decomposition | Matches — NEO-74 documented as client counterpart; no claim of prototype-complete player-visible craft. |
Register/tracking: E3.M2 In Progress is correct; no register bump required until Slice 3 closes (NEO-71).
Blocking issues
None.
Suggestions
-
Store-missing HTTP → 404 — Plan allows API test or README forDone. Addedinventory_store_missing/progression_store_missing. README documents 404 paths; engine tests cover progression store missing. Optional: add onePlayerCraftApiTestscase with a test double store returningInventoryStoreMissingto lock HTTP mapping (mirrorPlayerInventoryApiStoreMissing→ 404).PostCraft_WhenInventoryStoreMissing_ShouldReturnNotFoundwithInventoryStoreAlwaysMissingtest double viaWithWebHostBuilder. -
Wire request naming — Wire type isDone. Renamed wire DTO toCraftRequestwhile inventory usesPlayerInventoryMutationRequest. ConsiderPlayerCraftRequest(orCraftWireRequest) to avoid collision with the module doc’s conceptualCraftRequestcontract (actor + recipe + quantities) and to match player-scoped mutation DTO naming.PlayerCraftRequest. -
DefaultDone. Addedquantitytest — Plan adopts omitted/nullquantity→ 1. Add one API test POST withoutquantity(or explicitnull) asserting success with same outcome asquantity: 1.PostCraft_WhenQuantityOmitted_ShouldDefaultToOneAndSucceed. -
Bruno deny isolation —Done.Post craft deny insufficient materials.bruassumes empty bag ondev-local-1. On a persistent Postgres dev store, prior spine/inventory requests can seed scrap and cause a false pass. Document collection run order infolder.bruor add a pre-request inventory baseline assert (same pattern as NEO-63 delta-node isolation).folder.brudocuments run order; insufficient-materials request is seq 1 with pre-request empty-bag guard; spine moved to seq 5.
Nits
-
Nit:Done.E3M2-prototype-backlog.mdline “Adddocs/manual-qa/{NEO-XX}.mdwhen craft HTTP lands” is generic; NEO-70 kickoff explicitly waived manual QA — consider a footnote so future readers do not expectNEO-70.md. -
Nit: BrunoDone. Pre-request snapshots scrap total; test asserts quantity still 5.Post craft deny invalid quantity.brudoes not assert scrap quantity unchanged (API test does viaCountBagItem). -
Nit: README craft section has curl + reason table but no sample 200 success JSON body; a one-liner example would help Bruno adopters.Done.
Verification
cd server
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --filter "FullyQualifiedName~PlayerCraftApiTests"
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --filter "FullyQualifiedName~PlayerCraftApiTests|FullyQualifiedName~CraftOperationsTests"
Results (2026-05-24): 10 PlayerCraftApiTests pass; 21 craft-related tests (PlayerCraftApiTests + CraftOperationsTests) pass.
Full suite: 11 failures in AbilityCastApiTests (host startup / unrelated to this branch diff); 327 pass. Re-run full suite before merge if CI gates on it — failures appear pre-existing on main, not introduced by NEO-70.
Manual (Bruno):
- Start server; run
bruno/neon-sprawl-server/craft/Post craft gather refine make spine.bruagainstenvironments/Local.bru— 200,success: true,field_stim_mk0in inventory,refineXP ≥ 20. - Run deny requests in
craft/folder (inventory full / invalid quantity use inventory POST for setup only — per plan).