neon-sprawl/docs/reviews/2026-04-10-NEO-18.md

59 lines
6.2 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-18 (Camera integration hardening)
**Date:** 2026-04-10
**Scope:** Branch `NEO-18-camera-integration-hardening` against `origin/main`.
**Base:** `origin/main` (merge-base `01cfd4ec4d8f31a15bbaa0429e46ef0d2ddea4c8`).
## Verdict
**Approve with nits** — The camera hardening and contract/doc updates line up well with NEO-18 and E1.M2 intent, and I did not find a correctness break in the reviewed camera path. ~~Clean up the remaining plan/scope mismatches before merge so the branch tells one consistent story.~~ **Plan Technical approach + manual occluder note addressed post-review; HttpLogging chore left on branch per author (suggestion 2 declined).**
## Summary
This branch closes the E1.M2 prototype slice in a coherent way: `CameraState` is documented as a consumer-facing snapshot, E1.M2/E6.M2/module-register docs now agree on what is and is not part of that contract, and the occluder hardening avoids touching freed keys before restore. Moving the rig update to `_physics_process` with `process_physics_priority = 1` is a sensible follow-up to the jitter concern and matches the updated module documentation. Overall risk is low in the camera code itself. ~~The main remaining concerns are documentation drift inside the NEO-18 plan and an extra server observability change that is not tied back to the story scope.~~ **Resolved / noted:** plan drift fixed; server logging change acknowledged as intentionally bundled.
## Documentation checked
| Document | Assessment |
|----------|------------|
| `docs/plans/NEO-18-implementation-plan.md` | **Matches** — Scope, decisions, shipped notes, acceptance checklist, and Technical approach (`_physics_process` + priority) align with the branch. **Supplement:** plan now records **runtime nav mesh** behavior (`PARSED_GEOMETRY_BOTH`, reparent + rebake for dev smoke); see plan **Decisions** and **Runtime navigation (lesson for future mechanics)**. |
| `docs/decomposition/modules/E1_M2_IsometricCameraController.md` | **Matches** — Consumer contract, E1.M2 Ready status, yaw semantics, occlusion hardening, and the `_physics_process` update all reflect the implementation. |
| `docs/decomposition/modules/E6_M2_ConsentAndRiskUxSignals.md` | **Matches** — Camera-adjacent consumer guidance and authority split are consistent with the code and E1.M2 doc. |
| `docs/decomposition/modules/module_dependency_register.md` | **Matches** — E1.M2 is marked Ready with appropriate script/scene pointers. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **Matches** — E1.M2 tracking row reflects NEO-18 completion; no extra tracking-table update appears needed after this merge. |
| `docs/decomposition/epics/epic_01_core_player_runtime.md` | **Matches** — Slice 2 language now reflects “yaw fixed in UX” rather than “camera never rotates,” which is the right contract. |
| `docs/decomposition/modules/client_server_authority.md` | **Matches** — Camera remains client-local presentation only; no server gameplay authority was added. |
| `docs/decomposition/modules/contracts.md` | **Matches** — E1.M2s Ready status is reasonable for this prototype slice given the documented contract and consumer guidance. |
## Blocking issues
_None identified in the implementation reviewed._
## Suggestions
1. ~~`docs/plans/NEO-18-implementation-plan.md`: update the Technical approach bullet that still says `CameraState` is refreshed every `_process`. The same document now records the shipped behavior as `_physics_process` plus `process_physics_priority = 1`, so leaving both versions in the plan makes the acceptance source internally inconsistent.~~ **Done.**
2. ~~`server/NeonSprawl.Server/Program.cs` and `server/NeonSprawl.Server/appsettings.Development.json`: either split the ASP.NET HttpLogging removal into a separate chore or record the reason in the NEO-18 plan/PR. It is not part of the documented camera-hardening scope, and it reduces local request-path observability during the manual client/server checks described elsewhere in the repo.~~ **Intentionally not applied** (author keeps HttpLogging removal on this branch without plan/PR carve-out).
## Nits
- ~~`client/test/isometric_follow_camera_test.gd`: the new helper tests are worthwhile, but the actual purge/restore path is still effectively manual-only. If that editor verification has already been done, note it explicitly in the PR/story handoff so readers know the freed-occluder scenario was exercised end-to-end.~~ **Done.** — See `docs/plans/NEO-18-implementation-plan.md` §Tests manual verification (freed occluder, 2026-04-10).
## Verification
- Reviewed `git diff origin/main...HEAD` for the branch, with focused checks on `client/scripts/isometric_follow_camera.gd`, `client/scripts/camera_state.gd`, `client/test/isometric_follow_camera_test.gd`, and the updated E1.M2/E6.M2/module docs.
- `dotnet build NeonSprawl.sln` could not complete in this session because `server/NeonSprawl.Server/bin/Debug/net10.0/NeonSprawl.Server.dll` was locked by a running `dotnet.exe` process.
- Godot / GdUnit client tests were not run in this session.
- Before merge, I would still run: `dotnet build NeonSprawl.sln` with the dev server stopped, then the documented camera manual checks in `client/README.md` plus the NEO-18 plan step that frees an occluder while it is faded.
## Supplement — runtime navigation (2026-04-10)
**Context:** Dev smoke for the prototype `Obstacle` must match **click-to-move**, not only physics and occlusion.
**Lesson:** With Godots default **`NavigationMesh.geometry_parsed_geometry_type`** (**`PARSED_GEOMETRY_BOTH`**), **`MeshInstance3D` nodes under `NavigationRegion3D`** still contribute to bakes. **Disabling collision and rebaking** can leave the **nav hole** unchanged. **`main.gd`** fixes dev smoke by **reparenting** the obstacle **out of the region**, **rebaking**, and **`sync_navigation_agent_after_map_rebuild`**.
**Product impact:** Any future mechanic that **removes or opens world geometry** during play (barricades, doors, destroyed cover) must account for **nav source geometry**, not just **collision** — see `docs/plans/NEO-18-implementation-plan.md` (**Decisions** table row and **Runtime navigation** subsection).
---
*Review produced per [code review agent](.cursor/rules/code-review-agent.md).*