NEO-82: Add code review for cast combat wiring.

pull/116/head
VinPropane 2026-05-25 10:44:02 -04:00
parent e8be100f80
commit 0b40ae3598
1 changed files with 63 additions and 0 deletions

View File

@ -0,0 +1,63 @@
# Code review — NEO-82 Wire ability-cast into combat engine + cast response
**Date:** 2026-05-25
**Scope:** Branch `NEO-82-wire-ability-cast-combat-resolution` · commits `4126ac9``e8be100` vs `origin/main`
**Base:** `origin/main`
## Verdict
**Approve with nits**
## Summary
NEO-82 delivers **E5M1-07**: after E1.M4 cast gates pass, `AbilityCastApi` invokes `CombatOperations.TryResolve`, returns nested wire `combatResolution` on accept, maps combat denies (notably `target_defeated`) to JSON cast denies without cooldown commit, and replaces the global prototype cooldown with per-ability catalog `cooldownSeconds` on successful resolve. Handler flow, DTO shape, kickoff decisions (nested block, no cooldown on defeated deny), Bruno happy + defeat spine, and README updates align with the implementation plan. Four new integration tests cover zero-damage accept, four-pulse defeat chain, and guard-vs-pulse catalog cooldown; existing cast tests assert resolution fields. Residual risk is low — server-only HTTP extension with clear deny vocabulary; module tracking docs should sync on merge (same follow-up pattern as NEO-81).
## Documentation checked
| Document | Result |
|----------|--------|
| [`docs/plans/NEO-82-implementation-plan.md`](../plans/NEO-82-implementation-plan.md) | **Matches** — kickoff decisions, scope, acceptance checklist, expected cast table, and reconciliation align with code. |
| [`docs/plans/E5M1-prototype-backlog.md`](../plans/E5M1-prototype-backlog.md) · **E5M1-07** | **Matches** — AC checked, landed note added. |
| [`docs/decomposition/modules/E5_M1_CombatRulesEngine.md`](../decomposition/modules/E5_M1_CombatRulesEngine.md) | **Partially matches** — prototype notes paragraph updated for NEO-82; **Status** still reads “E5M1-07+ pending”; no **Related implementation slices** entry for E5M1-07 (NEO-78NEO-81 pattern). |
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) | **Partially matches** — E5.M1 note cites NEO-81; **NEO-82 landed** (cast → `combatResolution`, catalog cooldown) not yet added. |
| [`docs/decomposition/modules/documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) | **Partially matches** — E5.M1 row still ends with “accepts without **`CombatResolution`** yet”; should cite NEO-82 landed after merge. |
| [`docs/decomposition/modules/client_server_authority.md`](../decomposition/modules/client_server_authority.md) | **Matches** — server validates and computes all combat outcomes; client unchanged (NEO-85). |
| [`docs/decomposition/modules/pvp_combat_integration.md`](../decomposition/modules/pvp_combat_integration.md) | **N/A** — prototype NPC targets only. |
| [`server/README.md`](../../server/README.md) | **Matches** — cast `combatResolution` table, `target_defeated` deny, catalog cooldown policy, combat engine cross-link. |
| Full-stack epic decomposition | **Matches** — plan documents NEO-85 client counterpart; no Godot change in scope. |
| Manual QA | **N/A** — kickoff skip; Bruno + README sufficient. |
## Blocking issues
None.
## Suggestions
1. **Module doc status + slice entry** — In `docs/decomposition/modules/E5_M1_CombatRulesEngine.md`, update **Status** to cite **E5M1-07 NEO-82 landed** (cast POST returns `combatResolution`), and add **E5M1-07 (NEO-82)** under **Related implementation slices** linking the plan and README ability-cast section (mirror NEO-81 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-82 landed:** `AbilityCastApi``CombatOperations.TryResolve`, nested `combatResolution`, per-ability catalog cooldown, `target_defeated` cast deny.
3. **Bruno defeat spine HP stepping** — Plan §6 calls for asserting HP 75 → 50 → 25 → 0 on casts 14; `Post cast lock pulse defeat spine.bru` only captures/asserts the fourth cast. Consider storing intermediate cast bodies in pre-request and asserting `targetRemainingHp` on each pulse for stronger manual lock-in (non-blocking — fifth deny + integration test cover the critical path).
## Nits
- Nit: `PostAbilityCast_ShouldDefeatTargetAfterFourPulses_ThenDenyTargetDefeated` runs pulses 13 inside **Act**; move them to **Arrange** for stricter AAA (same note as NEO-81 four-pulse pattern).
- Nit: After successful `TryResolve`, `TryGetDefinition` runs again solely for `CooldownSeconds``CombatOperations` already loaded the definition internally. Acceptable for thin handler clarity; optional future refactor if catalog lookups become hot-path.
- Nit: `AbilityPrototypeCooldown.GlobalDuration` is `[Obsolete]` but retained — matches plan; no remaining runtime references in tests or accept path.
## Verification
```bash
cd /home/don/neon-sprawl/server
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj \
--filter "FullyQualifiedName~AbilityCastApiTests|FullyQualifiedName~CooldownSnapshotApiTests"
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj
```
**AbilityCast + CooldownSnapshot tests:** 22/22 passed (local run).
**Full suite:** 394/394 passed (local run).
**Bruno (manual):** Run `bruno/neon-sprawl-server/ability-cast/` folder — happy cast + defeat spine (seq 23, ~10s pre-request cooldown waits).