neon-sprawl/docs/reviews/2026-05-28-NEO-95.md

62 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Code review — NEO-95 (E5M2-09)
**Date:** 2026-05-28
**Scope:** Branch `NEO-95-npc-attack-resolve-player-hp` vs `origin/main` — commits `ac4e1df``b702d9f`
**Base:** `origin/main`
**Follow-up:** Suggestions and nits addressed in review follow-up commit; re-verified 2026-05-28.
## Verdict
**Approve**
## Summary
NEO-95 delivers the server slice cleanly: **`IPlayerCombatHealthStore`** (lazy-init 100 HP, damage floor, dev reset) mirrors the NPC **`ICombatEntityHealthStore`** pattern; **`NpcAttackOperations.TryResolveTelegraphComplete`** re-checks the threat holder before applying catalog **`attackDamage`**; **`NpcRuntimeOperations.AdvanceAll`** invokes resolve at telegraph windup completion and passes the store from **`NpcRuntimeSnapshotWorldApi`** poll; **`GET /game/players/{id}/combat-health`** gates on position row (same as cooldown snapshot) and returns schema v1 JSON. Dev fixture reset restores player HP. Tests cover store unit behavior, resolve no-ops, runtime windup→damage wiring, integration elite timeline (FakeClock 7.5 s → **75** HP), fixture reset, and Bruno smoke with documented **`sleep(8000)`**. Build succeeds; NEO-95-filtered tests pass (**40/40**). Server-only scope — client HUD correctly deferred to NEO-97.
## Documentation checked
| Path | Result |
|------|--------|
| `docs/plans/NEO-95-implementation-plan.md` | **Matches** — AC checkboxes done; dedicated GET (kickoff), damage flow, holder re-check, fixture reset, tests, and file list align with the diff. |
| `docs/plans/E5M2-prototype-backlog.md` (E5M2-09) | **Matches** — AC checked, landed note present; client counterpart NEO-97 called out. |
| `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | **Matches** — NEO-95 implementation section + README/Bruno links; Summary **Status** marks E5M2-09 landed. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **Matches** — E5.M2 row includes **NEO-95 landed** (`IPlayerCombatHealthStore`, combat-health GET, Bruno folder, plan/README links). |
| `docs/decomposition/modules/client_server_authority.md` | **Matches** — server owns NPC→player damage; client poll-only read model; no client damage math. |
| `server/README.md` | **Matches** — session player combat HP section, GET route, fixture reset note, npc-runtime snapshot cross-link updated. |
## Blocking issues
None.
## Suggestions
1. ~~**Update `documentation_and_implementation_alignment.md`** — Append **NEO-95 landed** to the E5.M2 tracking row (`IPlayerCombatHealthStore`, `NpcAttackOperations`, `GET …/combat-health`, Bruno `combat-health/`, plan/README links), matching NEO-87NEO-94 entries. Listed implicitly by review checklist; not in the NEO-95 plan “Files to modify” table but keeps alignment doc truthful.~~ **Done.** (`5efa5a4`)
2. ~~**E5.M2 Summary status table** — Mark **E5M2-09 / NEO-95 landed** inline in the module Summary **Status** row (currently jumps from “NEO-94 runtime snapshot GET landed” to “NEO-95 NPC attack resolve + player combat HP → E5M2-12” without “landed”).~~ **Done.** (`5efa5a4`)
## Nits
- ~~Nit: **`NpcBehaviorState.AttackExecute`** branch in `NpcRuntimeOperations` is unreachable in the normal windup→recover flow (NEO-93); harmless defensive path but could carry a one-line comment or be removed until a visible execute phase is needed.~~ **Done.** — one-line comment added.
- ~~Nit: **`PlayerCombatHealthApi.BuildResponse`** echoes normalized lowercase **`snapshot.PlayerId`** while **`CooldownSnapshotApi`** echoes the route `{id}` as provided — consistent for prototype lowercase ids; note if mixed-case route ids become a concern.~~ **Done.****`BuildResponse`** now echoes route **`{id}`** like cooldown snapshot.
- ~~Nit: Elite integration test **`GetPlayerCombatHealth_ShouldDecreaseAfterEliteTelegraphComplete`** performs setup GETs in **Arrange** (acceptable for multi-poll timeline); baseline/mid snapshot reads could move to **Assert** if stricter AAA parity with NEO-94 follow-up is desired.~~ **Done.** — poll GETs kept in **Arrange** as timeline drivers only; **`ReadFromJsonAsync`** verification confined to **Act**/**Assert**.
## Verification
```bash
# Build + NEO-95-related tests (passed 2026-05-28)
dotnet build NeonSprawl.sln
dotnet test server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj \
--filter "FullyQualifiedName~PlayerCombatHealth|FullyQualifiedName~NpcAttackOperations|FullyQualifiedName~CombatTargetFixture|FullyQualifiedName~NpcRuntimeOperations"
# Full server suite (Postgres persistence tests require local DB)
dotnet test server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj
# Bruno (CI runs entire bruno/neon-sprawl-server/)
# bruno/neon-sprawl-server/combat-health/
```
Manual: run **`Get combat health after elite attack.bru`** against dev server — after ~8 s wait, **`currentHp`** should be **75** with elite cast accepted on **`prototype_npc_elite`**.