NEO-81: Add code review for CombatOperations.TryResolve.
parent
ca15e7a689
commit
d63e448a08
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Code review — NEO-81 CombatOperations.TryResolve + CombatResult
|
||||||
|
|
||||||
|
**Date:** 2026-05-25
|
||||||
|
**Scope:** Branch `NEO-81-combat-operations-try-resolve` · commits `849c34f`–`ca15e7a` vs `origin/main`
|
||||||
|
**Base:** `origin/main`
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
**Approve with nits**
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
NEO-81 delivers **E5M1-06**: a static `CombatOperations.TryResolve` orchestrating `IAbilityDefinitionRegistry` and `ICombatEntityHealthStore` into a server-internal `CombatResult` envelope with stable deny codes (`unknown_ability`, `unknown_target`, `target_defeated`). Implementation matches kickoff decisions: self-contained id validation, defeated pre-check before any damage (including zero-damage utilities), zero-damage success via `TryGet` only, and `target_defeated` deny carrying `targetRemainingHp = 0` with `targetDefeated = false` on the envelope. Nine AAA unit/host tests cover pulse/guard/burst paths, four-pulse defeat chain, defeated re-hit deny, unknown ids, and DI resolution. `server/README.md` documents the combat engine section and NEO-82 handoff. Residual risk is low — server-only ops with no HTTP surface; module tracking docs and minor test/doc backlog sync should close on merge (same pattern as NEO-80 follow-ups).
|
||||||
|
|
||||||
|
## Documentation checked
|
||||||
|
|
||||||
|
| Document | Result |
|
||||||
|
|----------|--------|
|
||||||
|
| [`docs/plans/NEO-81-implementation-plan.md`](../plans/NEO-81-implementation-plan.md) | **Matches** — kickoff decisions, scope, acceptance checklist, reconciliation, and expected resolve table align with code. |
|
||||||
|
| [`docs/plans/E5M1-prototype-backlog.md`](../plans/E5M1-prototype-backlog.md) · **E5M1-06** | **Partially matches** — story AC checkboxes still unchecked; no landed note yet (E5M1-05 pattern). |
|
||||||
|
| [`docs/decomposition/modules/E5_M1_CombatRulesEngine.md`](../decomposition/modules/E5_M1_CombatRulesEngine.md) | **Partially matches** — Status still reads “E5M1-06+ pending”; no related implementation slice for NEO-81. |
|
||||||
|
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) | **Partially matches** — E5.M1 note stops at NEO-80; NEO-81 `CombatOperations` not cited. |
|
||||||
|
| [`docs/decomposition/modules/documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) | **Partially matches** — E5.M1 row should add NEO-81 landed after merge. |
|
||||||
|
| [`docs/decomposition/modules/client_server_authority.md`](../decomposition/modules/client_server_authority.md) | **N/A** — server-only internal ops; no client or HTTP changes. |
|
||||||
|
| [`docs/decomposition/modules/pvp_combat_integration.md`](../decomposition/modules/pvp_combat_integration.md) | **N/A** — prototype NPC targets only; PvP gates out of scope per plan. |
|
||||||
|
| [`server/README.md`](../../server/README.md) | **Matches** — combat engine section, reason-code table, `CombatResult` field semantics, NEO-82 cross-link. |
|
||||||
|
| Full-stack epic decomposition | **Matches** — plan documents no client counterpart; cast wire is NEO-82, HUD is NEO-85. |
|
||||||
|
| Manual QA | **N/A** — plan skip rationale holds (server-only internal ops). |
|
||||||
|
|
||||||
|
## Blocking issues
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Suggestions
|
||||||
|
|
||||||
|
1. **Module doc status line** — In `docs/decomposition/modules/E5_M1_CombatRulesEngine.md`, update **Status** to cite **E5M1-06 NEO-81 landed** (`CombatOperations.TryResolve` + `CombatResult`), and add a **Related implementation slices** entry linking the NEO-81 plan and README combat engine section (mirror NEO-80 landed pattern).
|
||||||
|
|
||||||
|
2. **Dependency register + alignment table** — Extend the **E5.M1 note** in `module_dependency_register.md` and the E5.M1 row in `documentation_and_implementation_alignment.md` with **NEO-81 landed:** `CombatOperations`, `CombatResult`, `CombatReasonCodes`, defeated pre-check deny vocabulary.
|
||||||
|
|
||||||
|
3. **E5M1 backlog landed note** — In `docs/plans/E5M1-prototype-backlog.md` § E5M1-06, check AC boxes and add a one-line landed note with link to [NEO-81 implementation plan](../plans/NEO-81-implementation-plan.md) (same pattern as E5M1-05).
|
||||||
|
|
||||||
|
4. **Defeated deny assertion parity** — `TryResolve_WithGuardOnDefeatedTarget_ShouldDenyWithTargetDefeated` omits `TargetRemainingHp` / `TargetDefeated` envelope assertions that the pulse defeated test covers; add `Assert.Equal(0, result.TargetRemainingHp)` and `Assert.False(result.TargetDefeated)` for consistency with the plan’s deny payload contract.
|
||||||
|
|
||||||
|
## Nits
|
||||||
|
|
||||||
|
- Nit: `TryResolve_WithFourPulses_ShouldDefeatTargetOnFourthResolve` runs four resolves in **Act**; pulses 1–3 are setup for the fourth outcome — move them to **Arrange** for stricter AAA (same note as NEO-80 four-pulse pattern).
|
||||||
|
|
||||||
|
- Nit: `CreatePrototypeRegistry` includes **`prototype_dash`** but no dash resolve test; guard covers the zero-damage path — optional dash smoke for catalog completeness.
|
||||||
|
|
||||||
|
- Nit: `TryApplyDamage` false branch maps to **`unknown_target`** — correct for current store contract (unknown/empty/negative damage), but if store semantics expand (e.g. internal failure), NEO-82 cast wiring may want a distinct ops-level code; defer until cast integration.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/don/neon-sprawl/server
|
||||||
|
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj \
|
||||||
|
--filter "FullyQualifiedName~CombatOperationsTests"
|
||||||
|
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj
|
||||||
|
```
|
||||||
|
|
||||||
|
**CombatOperationsTests:** 9/9 passed (local run).
|
||||||
|
|
||||||
|
**Full suite:** 389 passed, 1 failed — `TargetingApiTests.GetTarget_ShouldReturnNone_WhenNoLock` (unrelated to this branch; investigate separately if CI is green on `main`).
|
||||||
|
|
||||||
|
Manual: **N/A** — no HTTP or client surface in this story.
|
||||||
Loading…
Reference in New Issue