diff --git a/docs/plans/NEO-64-implementation-plan.md b/docs/plans/NEO-64-implementation-plan.md new file mode 100644 index 0000000..a70ecd9 --- /dev/null +++ b/docs/plans/NEO-64-implementation-plan.md @@ -0,0 +1,103 @@ +# 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 + +- [ ] Hook sites documented in code and `server/README.md`. +- [ ] 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, when **`depletionOutcome.Snapshot.RemainingGathers == 0`** (node exhausted by this gather): + - 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.