neon-sprawl/docs/reviews/2026-05-24-NEO-73.md

6.4 KiB
Raw Blame History

Code review — NEO-73 client gather feedback on interact

Date: 2026-05-24
Scope: Branch NEO-73-e3m1-client-gather-feedback-on-interact · commits 4682db4b52428e vs origin/main
Base: origin/main
Follow-up: Review feedback addressed in 434226a (interaction failure signal, docs, tests).

Verdict

Approve — implementation matches the adopted plan and E3S5-02 acceptance criteria; GdUnit suites pass. Follow-up cleared pending-gather flag on interact HTTP failures and updated alignment/module docs.

Summary

The branch adds skill_progression_client.gd, prototype_interactable_picker.gd, gather/skill HUD labels, interaction_result_received on interaction_request_client.gd, and orchestration in main.gd (nearest-node R, inventory delta on GatherFeedbackLabel, salvage row on SkillProgressionLabel, auto-refresh after successful gather). Four new GdUnit suites extend interaction and client coverage; manual QA and plan reconciliation are present. Design follows NEO-72/NEO-28 patterns (thin HTTP clients, _busy guard, authority config copy, HUD error lines). main.gd grows by ~150 lines but the picker and skill client are appropriately extracted.

Documentation checked

Document Result
docs/plans/NEO-73-implementation-plan.md Matches — kickoff decisions (nearest R, inventory delta copy, dedicated skill label) reflected; acceptance checklist and reconciliation complete.
docs/plans/E3S5-client-prototype-backlog.md · E3S5-02 Matches — acceptance criteria checked; scope/out-of-scope honored.
docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md Matches — NEO-73 client slice landed note added.
docs/decomposition/modules/documentation_and_implementation_alignment.md Matches — E3.M1 row notes NEO-73 landed; NEO-73 removed from E3.M3 Slice 5 remaining list.
docs/decomposition/modules/client_server_authority.md Matches — client displays outcomes; gather resolution stays server-side via POST …/interact.
docs/manual-qa/NEO-73.md Matches — four-node R walkthrough, depletion deny, terminal does not overwrite gather line, no manual I.
client/README.md gather feedback section Matches — endpoints, R nearest-node rule, HUD labels, boot hydrate; automated test Scope lists NEO-73 suites.
Full-stack epic decomposition Matches — E3S5-02 client counterpart for server NEO-63; Godot manual QA present; does not claim Slice 5 capstone (NEO-75) complete.

Blocking issues

(none)

Suggestions

  1. Update alignment register + E3.M1 module note on merge — Plan §8 explicitly deferred documentation_and_implementation_alignment.md E3.M1 row and E3.M1 client slice note until implementation completes. Add a NEO-73 landed clause (gather HUD, skill_progression_client, four-node R picker) and move NEO-73 off E3.M3 “remaining Slice 5” list. Done.

  2. Clear _pending_gather_feedback when interact HTTP failsInteractionRequestClient only emits interaction_result_received on parsed 200; non-200 paths print and return. main.gd sets _pending_gather_feedback = true in _forward_interact_resource_nearest before POST. If the request fails (500, malformed body, transport error), the flag stays set and a later inventory_received (e.g. manual I) can run _finalize_pending_gather_feedback with a stale _gather_pre_scrap_qty, painting a spurious gather delta. Clear the flag on non-200 (new signal or connect to a failure hook) or reset in _on_interaction_result_for_gather only after a matching success/deny 200. Done. interaction_request_failed signal + _on_interaction_request_failed_for_gather clears pending flag.

  3. client/README.md automated tests Scope — Add skill_progression_client.gd, prototype_interactable_picker.gd, and extended interaction_request_client_test.gd to the Scope bullet list (same discoverability fix as NEO-72). Done.

  4. GdUnit: deny path for gather refreshgather_feedback_refresh_test.gd only asserts the allow → dual-refresh path. A deny mock asserting zero inventory/skill sync calls would lock the “no refresh on node_depleted” contract. Done.

Nits

  • Nit: Gather: ok — inventory refresh failed (main.gd _on_inventory_sync_failed) reads as success; consider Gather: inventory refresh failed for clarity. Done.
  • Nit: Rapid R while a gather POST is in flight overwrites _gather_pre_scrap_qty; delta can be wrong if two responses complete out of order. Prototype-acceptable; last-wins POST coalescing already exists on the interaction client.
  • Nit: KNOWN_RESOURCE_NODE_IDS in prototype_interactable_picker.gd duplicates the frozen four-node table — intentional catalog-empty fallback; keep in sync with E3_M1 if ids ever change (they are frozen).

Verification

GdUnit (verified on review):

cd client
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode \
  -a res://test/skill_progression_client_test.gd \
  -a res://test/gather_feedback_refresh_test.gd \
  -a res://test/prototype_interactable_picker_test.gd \
  -a res://test/interaction_request_client_test.gd

16/16 passed; no PackedStringArray/PackedByteArray header conversion errors on skill_progression_client.gd (includes deny + HTTP failure tests after follow-up).

Manual (required before merge — real HTTPRequest + four nodes):

  1. cd server/NeonSprawl.Server && dotnet run
  2. Godot F5 — follow docs/manual-qa/NEO-73.md (boot salvage row, R at each anchor without I, depletion deny, terminal E leaves gather label unchanged).

Optional lint (pre-push hook parity):

gdlint client/scripts client/test
gdformat --check client/scripts client/test