# NEO-64 — Implementation plan ## Story reference | Field | Value | |--------|--------| | **Key** | NEO-64 | | **Title** | E3.M1: resource_gathered / gather_node_depleted telemetry hook sites | | **Linear** | https://linear.app/neon-sprawl/issue/NEO-64/e3m1-resource-gathered-gather-node-depleted-telemetry-hook-sites | | **Module** | [E3.M1 — ResourceNodeAndGatherLoop](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) · Epic 3 Slice 2 · backlog **E3M1-08** | | **Branch** | `NEO-64-e3m1-resource-gathered-gather-node-depleted-telemetry-hooks` | | **Blocked by** | [NEO-62](https://linear.app/neon-sprawl/issue/NEO-62) — `GatherOperations` + `GatherResult` (**Done** on `main`); [NEO-63](https://linear.app/neon-sprawl/issue/NEO-63) — interact gather wiring (**Done** on `main`). | | **Pattern** | [NEO-49](https://linear.app/neon-sprawl/issue/NEO-49) / [NEO-56](https://linear.app/neon-sprawl/issue/NEO-56) — comment-only engine anchors; [NEO-30](https://linear.app/neon-sprawl/issue/NEO-30) — event name + `TODO(E9.M1)` | ## Kickoff clarifications | Topic | Question | Agent recommendation | Answer | |--------|----------|----------------------|--------| | **Runtime behavior** | Comments-only vs dev `ILogger`? | **Comments-only** — `TODO(E9.M1)`; no production logging (NEO-49/56 precedent). | **Adopted** (repo precedent; not asked). | | **Hook anchor** | Engine only vs engine + `InteractionApi`? | **Engine only** in **`GatherOperations.TryGather`** — HTTP calls engine; no duplicate API-layer hooks (NEO-56 precedent). | **Adopted** (repo precedent; not asked). | | **`gather_node_depleted` timing** | Success transition vs also pre-gather deny? | **Success transition only** — hook when a successful gather drives **`remainingGathers`** to **0** after depletion commit; not on pre-gather **`node_depleted`** deny. | **User:** success transition only. | | **Manual QA doc** | `docs/manual-qa/NEO-64.md`? | **Yes** — NEO-49/56 verification pattern (comments + no behavior change). | **Adopted** (repo convention). | ## Goal, scope, and out-of-scope **Goal:** Document and place **comment-only hook sites** on the server-authoritative gather path for future E9.M1 catalog events **`resource_gathered`** and **`gather_node_depleted`**. Align with Epic 3 Slice 2 telemetry vocabulary ([epic_03 — Slice 2](../decomposition/epics/epic_03_crafting_economy.md#epic-3-slice-2)). **In scope (from Linear + [E3M1-08](E3M1-prototype-backlog.md#e3m1-08--resource_gathered--gather_node_depleted-telemetry-hook-sites)):** - Hook placement in **`GatherOperations.TryGather`** with **`TODO(E9.M1)`** comments. - **`server/README.md`** gather subsection + [E3_M1 module](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) hook note. - **`docs/manual-qa/NEO-64.md`** — verify comments + no behavior/API change. - E3M1 backlog + alignment register updates when implementation completes. **Out of scope (from Linear):** - Telemetry ingest, dashboards (**E9.M1**). - `ILogger` / metrics / dev-only log lines. - Duplicate hook comments in **`InteractionApi`** (NEO-56 engine-only precedent). - **`gather_node_depleted`** on pre-gather depletion deny or interact deny passthrough (kickoff decision). ## Acceptance criteria checklist - [x] Hook sites documented in code and `server/README.md`. - [x] Matches Epic 3 Slice 2 telemetry vocabulary (`resource_gathered`, `gather_node_depleted`). ## Technical approach 1. **Authoritative surface:** **`GatherOperations.TryGather`** — all gather side effects (inventory → XP → depletion) funnel here; **`InteractionApi`** delegates **`resource_node`** interact without duplicating hooks. 2. **Hook site A — `resource_gathered`:** Immediately before the successful **`GatherResult`** return (after depletion commit succeeds): - Comment block names future E9.M1 event **`resource_gathered`**. - **`TODO(E9.M1): catalog emit`** (NEO-49 / NEO-56 style). - Planned payload fields: `playerId`, `interactableId` (node instance key), `itemId`, `quantity` (from **`GatherGrantApplied`**), `skillId` + `xpAmount` + `sourceKind` (from **`GatherXpGrantSummary`**), `remainingGathers` after commit. 3. **Hook site B — `gather_node_depleted`:** On the same success path, inside `if (remainingAfterGather == 0)` after depletion commit (NEO-40 `level_up` conditional pattern): - Comment block names future E9.M1 event **`gather_node_depleted`**. - **`TODO(E9.M1): catalog emit`** — emit **once** when capacity transitions to zero via successful gather (not on pre-gather **`node_depleted`** deny). - Planned payload fields: `interactableId`, optional `playerId` (last gatherer), `maxGathers` from definition if needed at E9.M1 wiring time. 4. **Cross-references (minimal):** - Update **`GatherResult.cs`** xml summary → hook sites in **`TryGather`**. - **`GatherOperations`** class summary → NEO-64 telemetry hook anchor. - **`server/README.md`:** new **NEO-64 telemetry hooks** subsection under gather engine (after NEO-62 block). - **`E3_M1_ResourceNodeAndGatherLoop.md`:** NEO-64 landed line when complete. - **`documentation_and_implementation_alignment.md`** + **`module_dependency_register.md`:** E3.M1 row — E3M1-08 complete; module **Ready** when all Slice 2 backlog lands. - **`E3M1-prototype-backlog.md`:** E3M1-08 checkboxes + landed note. 5. **NEO-56 interaction:** Successful gather already triggers **`item_created`** inside **`PlayerInventoryOperations.TryAddStack`**; **`resource_gathered`** is the gather-funnel catalog event at the orchestration layer (analogous to cast accept vs ability-specific events). ## Files to add | Path | Purpose | |------|---------| | `docs/manual-qa/NEO-64.md` | Manual QA: verify comment blocks + no behavior/API change. | | `docs/plans/NEO-64-implementation-plan.md` | This plan. | ## Files to modify | Path | Rationale | |------|-----------| | `server/NeonSprawl.Server/Game/Gathering/GatherOperations.cs` | **NEO-64:** comment-only **`resource_gathered`** + **`gather_node_depleted`** hook sites on success path. | | `server/NeonSprawl.Server/Game/Gathering/GatherResult.cs` | Cross-reference hook anchor in type summary (no behavior change). | | `server/README.md` | Document gather telemetry hook placement (NEO-64). | | `docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md` | Implementation snapshot: NEO-64 telemetry hooks landed. | | `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E3.M1 row: NEO-64 hook sites. | | `docs/decomposition/modules/module_dependency_register.md` | E3.M1 note — E3M1-08 complete; module status **Ready**. | | `docs/plans/E3M1-prototype-backlog.md` | E3M1-08 checkboxes + landed note. | ## Tests | Test file | What it covers | |-----------|----------------| | *(none added or changed)* | Comments-only change; no behavior or wire contract change. Regression: `dotnet test NeonSprawl.sln` (existing **`GatherOperationsTests`** + **`InteractionResourceNodeGatherIntegrationTests`** unchanged). Manual verification: **`docs/manual-qa/NEO-64.md`**. | ## Open questions / risks | Question / risk | Agent recommendation | Status | |-----------------|----------------------|--------| | **Overlap with NEO-56 `item_created`** | Keep both — **`item_created`** at inventory layer; **`resource_gathered`** at gather orchestration (E9.M1 can correlate or sample). | **adopted** | | **Pre-gather depletion deny telemetry** | No **`gather_node_depleted`** hook on deny; interact **`node_depleted`** **`reasonCode`** suffices until E9.M1 defines a separate deny funnel event. | **adopted** (kickoff) | None blocking.