# 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.
