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