neon-sprawl/docs/reviews/2026-05-24-NEO-63.md

69 lines
6.6 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-63 interact path integration + NEO-62 gather engine
**Date:** 2026-05-24
**Scope:** Branch `NEO-63-e3m1-interact-path-integration-registry-expansion-bruno` · commits `acf9fe1``01dc48a` vs `origin/main` (includes stacked **NEO-62** gather engine commits)
**Base:** `origin/main`
## Verdict
**Approve with nits**
## Summary
This branch completes Epic 3 Slice 2 gather wiring: **NEO-62** adds **`GatherOperations.TryGather`** and **`GatherResult`** (inventory → XP → depletion commit, compensating rollback on XP failure); **NEO-63** replaces the NEO-41 silent inline XP path in **`InteractionApi`** with gather-engine delegation, expands **`PrototypeInteractableRegistry`** to four resource-node anchors, and adds HTTP integration tests, Bruno requests, and manual QA. Denies now surface as **`allowed: false`** with stable **`reasonCode`** passthrough (`node_depleted`, `inventory_full`, skill deny codes, etc.). Risk is moderate but well-contained: the orchestration touches inventory, progression, and depletion stores with ordered commits and rollback, but coverage is strong (6 engine tests, 4 new HTTP integration tests, registry/world API updates, Bruno depletion loop). Recommend merging **NEO-62** first or accepting a stacked PR with both stories called out in the PR title/body.
## Documentation checked
| Document | Result |
|----------|--------|
| [`docs/plans/NEO-62-implementation-plan.md`](../plans/NEO-62-implementation-plan.md) | **Matches** — commit order (inventory → XP → depletion), rollback on XP failure, reason-code passthrough, **`GatherResult`** shape, acceptance checklist complete. |
| [`docs/plans/NEO-63-implementation-plan.md`](../plans/NEO-63-implementation-plan.md) | **Matches****`InteractionApi`** delegates **`resource_node`** to gather engine; NEO-41 inline XP removed; four registry anchors at kickoff coords; integration + Bruno + manual QA; acceptance checklist complete. |
| [`docs/plans/E3M1-prototype-backlog.md`](../plans/E3M1-prototype-backlog.md) | **Matches** — E3M1-06 and E3M1-07 landed notes and checkboxes updated. |
| [`docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md`](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) | **Matches** — four-node freeze table anchors, NEO-62 engine + NEO-63 interact slices, **`GatherResult`** field table, E1.M3 boundary (no separate gather HTTP). |
| [`docs/decomposition/modules/documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) | **Matches** — E3.M1 row notes NEO-62/NEO-63 landed; NEO-64 telemetry still planned. |
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) | **Matches****E3.M1 note** extended through E3M1-07; row **In Progress** until NEO-64. |
| [`docs/decomposition/modules/client_server_authority.md`](../decomposition/modules/client_server_authority.md) | **Matches** — server resolves gather outcomes; client sends interact intent only. |
| [`docs/manual-qa/NEO-63.md`](../manual-qa/NEO-63.md) | **Matches** — happy path, depletion deny, terminal control, Bruno pointers. |
| [`server/README.md`](../../server/README.md) | **Matches** — gather engine subsection, gather-via-interact reason codes, four anchors. |
Register/tracking: alignment table and **E3.M1 note** updated for NEO-62/NEO-63.
## Blocking issues
None.
## Suggestions
1. ~~**AAA layout in depletion integration test** — `InteractionResourceNodeGatherIntegrationTests.PostInteract_ResourceNode_WhenDepleted_ShouldDenyWithNodeDepletedWithoutGrants` asserts **`Allowed`** inside the Arrange loop (10 pre-gathers). Move loop invariants to **Act** (fire-and-forget or collect last response) and keep all expectations in **Assert**, per [csharp-style](../../.cursor/rules/csharp-style.md#unit-and-integration-tests-arrange-act-assert) — same pattern flagged on NEO-61.~~ **Done.** — pre-gather loop moved to **Act**; **`Assert.All`** on successful responses; depletion + totals asserted in **Assert**.
2. ~~**Tighten skill-deny integration assertion** — `PostInteract_ResourceNode_WhenSalvageDisallowsActivity_ShouldDenyWithoutGrants` only checks `ReasonCode` is non-empty. Assert the expected passthrough **`source_kind_not_allowed`** (from `SkillProgressionSnapshotApi.ReasonSourceKindNotAllowed`) so regressions in XP rollback + reason mapping are caught explicitly.~~ **Done.**
3. ~~**Optional HTTP `inventory_full` deny** — Engine test covers full-bag deny without depletion; no interact-level test yet. Low priority since **`GatherOperationsTests`** already locks the rule; add one HTTP test if you want parity with depletion deny at the wire boundary.~~ **Done.****`PostInteract_ResourceNode_WhenBagFull_ShouldDenyWithInventoryFullWithoutDepletingNode`**.
4. ~~**Test duplication (optional cleanup)** — `InteractionResourceNodeGatherXpTests` overlaps with `InteractionResourceNodeGatherIntegrationTests` on terminal control and alpha XP smoke. Plan kept both for regression; consider trimming the legacy file to terminal-only once NEO-63 integration tests are trusted.~~ **Done.** — legacy file trimmed to terminal-only; **`InteractionApiTests`** summary points at NEO-63 integration tests.
## Nits
- ~~Nit: **`SkillProgressionGrantOperations`** summary still references “NEO-41 gather interact hook”; interact now routes through **`GatherOperations`** (NEO-63).~~ **Done.**
- ~~Nit: **`GatherOperations`** class comment says “Interact wiring is NEO-63” — wiring is landed; update to “wired from **`InteractionApi`** (NEO-63)”.~~ **Done.**
- Nit: Stacked branch — five commits include NEO-62 plan + engine before NEO-63 wiring. Call out both issue keys in the PR title/description so reviewers know scope.
- Nit: Beta/gamma/delta per-node yield quantities (2/3/5) are validated in **`GatherOperationsTests`** (delta) but not via HTTP interact on the new anchors; acceptable for prototype, optional Bruno spot-check on delta.
## Verification
```bash
cd server
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --filter "FullyQualifiedName~Gather|FullyQualifiedName~Interaction"
# Passed! Failed: 0, Passed: 70
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj
# Passed! Failed: 0, Passed: 292
```
Manual / Bruno: run `docs/manual-qa/NEO-63.md` curl steps; Bruno folder `bruno/neon-sprawl-server/interaction/`**`Post interact gather then get inventory.bru`**, **`Post interact depleted node deny.bru`**.
Postgres persistence (when `ConnectionStrings__NeonSprawl` is set): gather depletion rows should persist across restarts via existing NEO-61 store (not re-tested in this diff).