neon-sprawl/docs/reviews/2026-05-25-NEO-85.md

5.5 KiB
Raw Blame History

Code review — NEO-85 client combat feedback + target HP HUD

Date: 2026-05-25
Scope: Branch NEO-85-client-combat-feedback-hp-hud · commits f03e689faf3fe1 vs origin/main
Base: origin/main

Verdict

Approve with nits

Summary

NEO-85 delivers E5M1-11: the Godot client parses nested combatResolution on cast accept, extends CastFeedbackLabel with damage/defeat copy, adds CombatTargetsClient for GET /game/world/combat-targets, and wires CombatTargetHpLabel with event-driven refresh on successful cast and target lock change. Implementation follows NEO-28/NEO-73 thin-client patterns (injectable HTTP, _busy guard, harness tests) and keeps combat math server-authoritative. Fifteen new/extended GdUnit cases pass locally. Residual risk is low — client-only HUD wiring atop landed NEO-82/NEO-83 server contracts; brief HP label lag between cast feedback and GET completion is an adopted trade-off.

Documentation checked

Document Result
docs/plans/NEO-85-implementation-plan.md Matches — kickoff decisions (event-driven refresh, extend CastFeedbackLabel, dedicated HP label), scope, acceptance checklist, and reconciliation align with code.
docs/plans/E5M1-prototype-backlog.md · E5M1-11 Matches — AC checked; landed note cites clients + manual QA.
docs/decomposition/modules/E5_M1_CombatRulesEngine.md Matches — Status line cites E5M1-11 NEO-85 landed; Related implementation slices includes E5M1-11 entry. Done.
docs/decomposition/modules/module_dependency_register.md Matches — E5.M1 note cites NEO-85 landed; capstone E5M1-12 NEO-86 pending. Done.
docs/decomposition/modules/documentation_and_implementation_alignment.md Matches — E5.M1 row cites NEO-85 landed + manual QA + client README.
docs/decomposition/modules/client_server_authority.md Matches — no client-side damage math; HP from server GET snapshot; cast feedback from wire fields only.
docs/decomposition/modules/documentation_and_implementation_alignment.md · E1.M4 Partially matches — NEO-85 extends ability_cast_client.gd / CastFeedbackLabel (E1.M4 cast funnel) but E1.M4 row stops at NEO-32; optional cross-reference only.
client/README.md Matches — combat HUD subsection documents cast resolution copy, HP label, refresh triggers, dev fixture reset.
docs/manual-qa/NEO-85.md Matches — Godot steps cover lock → cast → HP stepping → defeat → deny → clear lock; regression bullets for NEO-28/NEO-32.
Full-stack epic decomposition Matches — client half of NEO-82/NEO-83 stack; manual QA is Godot-first (not Bruno-only).

Blocking issues

None.

Suggestions

  1. Stale module_dependency_register E5.M1 note — Replace Pending client: E5M1-11 NEO-85 with a NEO-85 landed one-liner (mirror the E5.M1 alignment row: combat_targets_client.gd, CombatTargetHpLabel, event-driven GET, manual QA link). Done.

  2. Missing E5M1-11 Related implementation slice — Add an E5M1-11 (NEO-85) bullet under Related implementation slices in E5_M1_CombatRulesEngine.md (client combat HUD; link plan, manual QA, client/README.md combat section) so module readers stay consistent with E5M1-04E5M1-10 entries. Done.

  3. HTTP error-path test gap — Plan §Tests lists 404/invalid schema failure for combat_targets_client_test.gd; implementation covers schema mismatch only. Add a mock 404 (or non-2xx) case asserting targets_sync_failed with HTTP 404 for parity with other world GET clients. Done.

Nits

  • Nit: CombatRefreshHarness duplicates _locked_target_id_from_state / refresh logic from main.gd rather than testing main.gd directly — matches craft_feedback_refresh_test.gd precedent; acceptable for prototype scope.

  • Nit: Clearing lock (Esc) still fires a combat-target GET even though the HP label already renders no lock — harmless extra HTTP; skip refresh when new_lock is empty if chatter matters later.

  • Nit: Between cast accept and GET completion, CastFeedbackLabel shows authoritative targetRemainingHp from combatResolution while CombatTargetHpLabel may still show the prior snapshot row — adopted plan trade-off; optional future polish could render HP from resolution until GET lands.

Verification

cd /home/don/neon-sprawl/client
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode \
  -a res://test/ability_cast_client_test.gd \
  -a res://test/combat_targets_client_test.gd \
  -a res://test/combat_feedback_refresh_test.gd

GdUnit (local run): 16/16 passed (8 + 5 + 3 suites).

Manual: docs/manual-qa/NEO-85.md — server + Godot; Tab lock prototype_target_alpha, digit cast prototype_pulse, verify cast resolution copy and HP label stepping through defeat; optional POST /game/__dev/combat-targets-fixture between runs.