7.0 KiB
Code review — NEO-69 CraftResult engine
Date: 2026-05-24
Scope: Branch NEO-69-craftresult-engine-inputs-outputs-refine-xp · commits bba1415–308de6b vs main
Base: main
Verdict
Approve with nits — implementation matches the adopted plan and E3M2-05 acceptance criteria; ready for NEO-70 HTTP wiring after minor test-layout cleanup.
Summary
The branch adds CraftOperations.TryCraft and supporting CraftResult / reason-code types under server/NeonSprawl.Server/Game/Crafting/, plus read-only inventory helpers GetHeldQuantity and TrySimulateAddStack on PlayerInventoryOperations. The orchestration flow follows the kickoff decisions: quantity validation, recipe resolve, input pre-flight, output simulation on a cloned snapshot (no input consumption when full), commit remove → add → refine XP, and compensating rollback on output or XP failure. Eight AAA integration tests cover the primary deny paths, equipment routing, and XP rollback. Docs (plan, module snapshot, backlog, alignment, README) are updated and E3M2-05 is marked landed. Risk is low for prototype scope; the main gaps are test AAA layout (verification reads in Act) and missing coverage for quantity > 1 scaling.
Documentation checked
| Document | Result |
|---|---|
docs/plans/NEO-69-implementation-plan.md |
Matches — pre-flight output strategy, craft-layer reason codes, quantity parameter, XP rollback, engine-only scope, NEO-74 cross-link, acceptance checklist complete. Helper named TrySimulateAddStack instead of plan’s CanPlaceStack — equivalent behavior. |
docs/plans/E3M2-prototype-backlog.md · E3M2-05 |
Matches — acceptance criteria checked; landed note present. |
docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md |
Matches — implementation snapshot + CraftResult field table; NEO-69 craft engine documented. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
Matches — E3.M2 row notes NEO-69 landed. |
docs/decomposition/modules/module_dependency_register.md |
Matches — E3.M2 Status updated to In Progress (was Planned). |
docs/decomposition/modules/client_server_authority.md |
Matches (planned) — server-authoritative inventory mutation; no client in this story. |
NEO-62 / NEO-64 reference — GatherOperations |
Matches — same SkillProgressionGrantOperations.TryApplyGrant + compensating rollback pattern; craft correctly uses TryApplyGrant (not void RefineActivitySkillXpGrant) so XP deny can roll back inventory. |
| Full-stack epic decomposition | Matches — NEO-74 client counterpart linked; no claim of prototype-complete player-visible craft. |
Register/tracking: optional E3.M2 Status → In Progress in dependency register when authors next touch alignment docs. Done. E3.M2 Status is In Progress in module_dependency_register.md.
Blocking issues
None.
Suggestions
-
AAA — move verification reads to Assert — InDone. Verification reads moved to Assert in allCraftOperationsTests,TryGetSnapshot/GetXpTotalsafterTryCraftsit in the Act block; csharp-style requires outcome verification reads in Assert (Act = SUT invocation only). Mirror the rule even thoughGatherOperationsTestshas the same legacy layout.CraftOperationsTestsmethods. -
TestDone.quantity > 1scaling — Plan and kickoff adopt batchquantitywith scaled I/O. Add one test (e.g.refine_scrap_standardatquantity: 2) asserting 10× scrap consumed and 2× plate granted.TryCraft_RefineScrapStandard_AtQuantityTwo_ShouldScaleInputsAndOutputs. -
Defensive rollback on partial input removal — IfDone. Input loop tracksTryRemoveStackfails mid-loop after earlier inputs were removed (e.g.inventory_store_missing),TryCraftreturns deny without restoring already-removed inputs. Pre-flight makesDeniedunlikely; consider compensating restore for symmetry with output/XP rollback paths.inputsRemovedand callsCompensatingRestoreInputson mid-loop deny. -
OptionalDone. Two tests: bag-full deny for bag item; equipment route succeeds when bag full.TrySimulateAddStackunit test — Plan listed an optional dedicated test inPlayerInventoryOperationsTests; one bag-full vs equipment-route case would lock pre-flight simulation toTryAddStackbehavior without a full craft scenario.
Nits
-
Nit:Done.module_dependency_register.mdE3.M2 Status (Planned) lagsE3_M2_RefinementAndRecipeExecution.md(In Progress). -
Nit: No test for whitespace-onlyDone.recipeId(maps tounknown_recipevia trim — fine behavior, low priority).TryCraft_ForWhitespaceRecipeId_ShouldDenyWithUnknownRecipe. -
Nit: Compensating helpers discard rollback outcomes (
_ = TryAddStack/TryRemoveStack) — same as gather engine; acceptable for prototype.
Bugbot follow-up (PR #103)
-
Output pre-flight ignores input removal — simulate output placement on snapshot with inputs still present; falseDone. Pre-flight nowinventory_fullwhen input removal would free slots.TrySimulateRemoveStackall inputs on clone before output simulation; testTryCraft_WhenBagFullWithScrapInputs_ShouldSucceedByFreeingInputSlots. -
Missing output rollback in store-missing branch — output-addDone.StoreMissingrestored inputs but not partial outputs.CompensatingRemoveOutputsadded before input restore on that path. -
Rollback order on output deny —Done. Swapped to remove outputs first, then restore inputs (matchesDeniedbranch restored inputs before removing partial outputs, soTryAddStackcould fail silently when bag was full.StoreMissingand XP-failure paths).
Verification
cd server
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --filter "FullyQualifiedName~CraftOperationsTests"
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj
Results (2026-05-24): all 8 CraftOperationsTests pass. Full suite reported 2 failures in SkillProgressionGrantApiTests (unrelated to this branch; NEO-69 diff does not touch that file).
Results (post-review follow-up): 11 CraftOperationsTests + 2 new TrySimulateAddStack tests pass; full suite 328 tests pass.
Manual: none required for engine-only story — NEO-70 owns HTTP / Bruno verification.