5.2 KiB
Code review — NEO-120 (E7M1-09)
Date: 2026-06-07
Scope: Branch NEO-120-quest-accept-post vs origin/main — commits b22b68a … 9fe6cb3
Base: origin/main
Verdict
Approve with nits
Summary
NEO-120 adds POST /game/players/{id}/quests/{questId}/accept — a thin HTTP wrapper over landed QuestStateOperations.TryAccept. The handler mirrors QuestProgressApi (trim id → position-store 404), accepts optional v1 body with schemaVersion 0 treated as omitted, and returns a versioned QuestAcceptResponse (accepted, optional reasonCode, optional quest row) with HTTP 200 on structured denies (inventory/craft precedent). Row projection is shared via extracted QuestProgressApi.MapQuestProgressRow(QuestStepState) overloads so GET and POST stay aligned. Eight AAA integration tests cover player 404 gates, happy accept (with GET confirmation), omitted body, invalid schema, prerequisite deny, duplicate accept snapshot, and unknown quest. Three Bruno accept spine requests complement the existing quest-progress folder. Plan reconciliation, server/README.md, and E7.M1 decomposition docs are updated. Server-only slice — Godot accept HUD correctly deferred to NEO-122. Risk is low; no blocking correctness or contract issues found.
Documentation checked
| Path | Result |
|---|---|
docs/plans/NEO-120-implementation-plan.md |
Matches — kickoff decisions adopted (accepted naming, shared mapper, optional body v1); acceptance checklist checked; reconciliation section accurate. |
docs/plans/E7M1-prototype-backlog.md (E7M1-09) |
Matches — acceptance checkboxes checked; client counterpart NEO-122 noted. |
docs/decomposition/modules/E7_M1_QuestStateMachine.md |
Matches — NEO-120 accept POST bullet + README/Bruno links. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
Matches — E7M1-09 / NEO-120 landed note appended. |
docs/decomposition/modules/module_dependency_register.md |
Matches — NEO-120 note appended. |
server/README.md |
Matches — route, optional body rules, response table, curl sample, wiring note. |
| Full-stack epic decomposition | Matches — server-only story; plan explicitly defers Godot to NEO-122; does not claim prototype slice complete. |
Register / tracking: E7.M1 row correctly remains Planned until client capstone (NEO-123); no further register status change required for this merge.
Blocking issues
(none)
Suggestions
-
HTTP test forDone.already_completeddeny with snapshot — The plan response table and README documentalready_completedreturningaccepted: falseplus aquestrow. Integration tests coveralready_activeand operations-layerTryAccept_ShouldDenyAlreadyCompletedexists inQuestStateOperationsTests, but no HTTP test exercisesMapResponsefor a completed row (statuscompleted, optionalcompletedAt). A single test completing gather intro then re-accepting would lock the wire contract for NEO-122.PostQuestAccept_ShouldDenyAlreadyCompleted_WhenQuestAlreadyCompletedinQuestAcceptApiTests.cs. -
EmptyDone.{}body test — README documents{}as valid (defaultschemaVersion0). Tests cover omitted body and explicitschemaVersion: 1; a one-liner POST withStringContent("{}")would close the loop on the documented third valid shape.PostQuestAccept_ShouldReturnAcceptedTrue_WhenRequestBodyIsEmptyObjectinQuestAcceptApiTests.cs.
Nits
-
Nit:
MapQuestProgressRow(QuestStepState)throwsInvalidOperationExceptionon unknownQuestProgressStatus(would surface as 500). Inherited defensive guard from NEO-119; acceptable for prototype. -
Nit:Done. Arrange seeds active row without assertions; assert uses step index 0.PostQuestAccept_ShouldDenyAlreadyActive_WhenQuestAlreadyAcceptedasserts on the first accept response inside Arrange (lines 159–162). Works fine; optional move offirstBodychecks into Assert if strict AAA phase purity is desired. -
Nit: Bruno
Accept duplicate.brupre-request re-accepts gather intro (idempotent when seq 3 ran). Correct for standalone runs; collection order matters only when hitting a persistent dev server without reset.
Verification
# Primary signal for this story
cd server
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj \
--filter "FullyQualifiedName~QuestAcceptApiTests"
# Quest module regression
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj \
--filter "FullyQualifiedName~NeonSprawl.Server.Tests.Game.Quests"
# Full server suite (CI)
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj
Bruno: run the three accept requests in bruno/neon-sprawl-server/quest-progress/ (seq 3–5) against a local server with fresh dev-local-1.
Reviewer note: All 8 QuestAcceptApiTests and 93 quest-namespace tests passed locally during review. Follow-up: suggestions 1–2 and nit on already_active AAA addressed — 10 QuestAcceptApiTests pass.
Manual: None required beyond Bruno for this server-only story; Godot accept binding verification belongs to NEO-122.