# NEO-63 — Implementation plan ## Story reference | Field | Value | |--------|--------| | **Key** | NEO-63 | | **Title** | E3.M1: Interact path integration + registry expansion + Bruno | | **Linear** | https://linear.app/neon-sprawl/issue/NEO-63/e3m1-interact-path-integration-registry-expansion-bruno | | **Module** | [E3.M1 — ResourceNodeAndGatherLoop](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) · Epic 3 Slice 2 · backlog **E3M1-07** | | **Branch** | `NEO-63-e3m1-interact-path-integration-registry-expansion-bruno` | | **Precursors** | [NEO-62](https://linear.app/neon-sprawl/issue/NEO-62) — `GatherOperations` + `GatherResult` (**Done** on story branch); [NEO-41](https://linear.app/neon-sprawl/issue/NEO-41) — inline XP on interact (superseded) | ## Kickoff clarifications | Topic | Question | Agent recommendation | Answer | |--------|----------|----------------------|--------| | **New node anchors** | X/Z for beta, gamma, delta? | **Beta (18, −6)**, **gamma (12, 0)**, **delta (18, 0)** — spread from alpha **(12, −6)** and terminal **(0, 0)**, same radius **3**. | **Adopted** (no blocking questions at kickoff; user said **begin**). | | **Gather deny on interact** | `Allowed: false` + `reasonCode` vs NEO-41 silent success? | **`Allowed: false`** + passthrough gather **`reasonCode`** per E3M1-07 backlog. | **Adopted**. | | **InteractionResponse payload** | Extend with gather fields? | **Keep placeholder** — verify via GET inventory / skill-progression (module doc). | **Adopted**. | ## Goal, scope, and out-of-scope **Goal:** Wire **`POST …/interact`** **`resource_node`** path to **`GatherOperations`**; register **four** prototype nodes; Bruno + manual QA for gather → inventory and depletion deny. **In scope:** `InteractionApi`, `PrototypeInteractableRegistry` expansion, integration tests, Bruno, README, manual QA. **Out of scope:** Godot HUD, gather HTTP route, telemetry hooks (NEO-64). ## Acceptance criteria checklist - [x] In-range interact on fresh node increases inventory quantity and **`salvage`** XP. - [x] Depleted node returns **`node_depleted`** without inventory or XP change. - [x] Terminal interact unchanged (no gather side effects). - [x] Bruno documents happy path + depletion deny for at least one node. ## Technical approach 1. **`InteractionApi`:** On **`resource_node`** kind after range check, call **`GatherOperations.TryGather`**. Success → **`Allowed: true`**; deny → **`Allowed: false`** + **`ReasonCode`** from **`GatherResult`**. Remove NEO-41 inline **`SkillProgressionGrantOperations`** block. 2. **Registry:** Add beta/gamma/delta ids + anchors (see kickoff table). **`GET …/interactables`** returns **5** rows (terminal + four nodes). 3. **Tests:** New **`InteractionResourceNodeGatherIntegrationTests`** — inventory + XP success, depletion deny, terminal control, stub-registry deny. Update **`InteractablesWorldApiTests`** for four nodes. Trim NEO-41 XP-only defensive test (migrated to integration deny test). Fix **`SalvageActivityDeniedRegistryWebApplicationFactory`** for gather DI (items + resource nodes + inventory + instance stores). 4. **Bruno:** Update alpha gather request; add gather → GET inventory; add depletion deny (pre-request 10 gathers). 5. **Docs:** README interaction reason codes + gather subsection; E3.M1 module freeze table anchors; alignment + backlog checkboxes. ## Files to add | Path | Purpose | |------|---------| | `server/NeonSprawl.Server.Tests/Game/Interaction/InteractionResourceNodeGatherIntegrationTests.cs` | HTTP integration: inventory, XP, depletion, terminal, deny registry. | | `bruno/neon-sprawl-server/interaction/Post interact gather then get inventory.bru` | Happy path: interact + assert scrap in bag. | | `bruno/neon-sprawl-server/interaction/Post interact depleted node deny.bru` | Pre-loop 10 gathers on **`prototype_urban_bulk_delta`** (isolated from alpha requests earlier in collection); 11th **`node_depleted`**. | | `docs/manual-qa/NEO-63.md` | curl gather → inventory + depletion deny. | | `docs/plans/NEO-63-implementation-plan.md` | This plan. | ## Files to modify | Path | Rationale | |------|-----------| | `server/NeonSprawl.Server/Game/Interaction/InteractionApi.cs` | Delegate **`resource_node`** to **`GatherOperations`**; map denies. | | `server/NeonSprawl.Server/Game/Interaction/PrototypeInteractableRegistry.cs` | Three new node anchors + registry entries. | | `server/NeonSprawl.Server.Tests/Game/Interaction/InteractablesWorldApiTests.cs` | Assert **5** interactables + new anchor coords. | | `server/NeonSprawl.Server.Tests/Game/Interaction/InteractionResourceNodeGatherXpTests.cs` | Remove obsolete NEO-41 silent-deny test; keep terminal + XP smoke. | | `server/NeonSprawl.Server.Tests/Game/Interaction/SalvageActivityDeniedRegistryWebApplicationFactory.cs` | Full in-memory gather dependencies for deny test host. | | `bruno/neon-sprawl-server/interaction/Post interact prototype resource node gather.bru` | NEO-63 docs (inventory + XP via gather engine). | | `server/README.md` | Gather via interact; reason codes; four anchors. | | `docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md` | Anchor coords; NEO-63 slice bullet. | | `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E3.M1 row — NEO-63 landed. | | `docs/decomposition/modules/module_dependency_register.md` | E3.M1 note — E3M1-07 complete. | | `docs/plans/E3M1-prototype-backlog.md` | E3M1-07 checkboxes. | ## Tests | Test file | What it covers | |-----------|----------------| | `InteractionResourceNodeGatherIntegrationTests.cs` | Success: +1 scrap, +10 salvage XP. Depletion: 10 gathers then **`node_depleted`**. Terminal: no side effects. Stub registry: deny + no grants. | | `InteractablesWorldApiTests.cs` | **5** descriptors; beta/gamma/delta anchors. | | `InteractionResourceNodeGatherXpTests.cs` | Terminal + alpha XP smoke (regression). | ## Open questions / risks | Question / risk | Agent recommendation | Status | |-----------------|----------------------|--------| | **Branch stacks NEO-62** | PR base includes gather engine commits until NEO-62 merges. | **adopted** | | **Client world props** | Godot **`interactable_world_builder`** picks up new ids from GET; no scene edit required for server QA. | **deferred** | None blocking.