# NEO-130 — Implementation plan ## Story reference | Field | Value | |--------|--------| | **Key** | NEO-130 | | **Title** | E7M2-07: Reward telemetry hook sites (comment-only) | | **Linear** | https://linear.app/neon-sprawl/issue/NEO-130/e7m2-07-reward-telemetry-hook-sites-comment-only | | **Module** | [E7.M2 — RewardAndUnlockRouter](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md) · Epic 7 Slice 2 · backlog **E7M2-07** | | **Branch** | `NEO-130-reward-telemetry-hooks` | | **Precursor** | [NEO-128](https://linear.app/neon-sprawl/issue/NEO-128) **`Done`** — `QuestStateOperations.TryMarkComplete` → **`RewardRouterOperations`** (deliver-then-mark) | | **Pattern** | [NEO-121](NEO-121-implementation-plan.md) / [NEO-49](NEO-49-implementation-plan.md) — comment-only engine anchors + **`TODO(E9.M1)`**; engine-only (no duplicate API-layer hooks) | | **Client counterpart** | none (infrastructure-only per [E7M2-07](E7M2-prototype-backlog.md#e7m2-07--reward-telemetry-hook-sites-comment-only)) | ## Kickoff clarifications **No clarifications needed.** [E7M2-07](E7M2-prototype-backlog.md#e7m2-07--reward-telemetry-hook-sites-comment-only) backlog, Linear AC, landed router + quest wiring (NEO-127/128), and comment-only telemetry precedents (NEO-49/121) settle scope: - **Runtime behavior:** comments-only + **`TODO(E9.M1)`** — no production ingest, no **`ILogger`** (NEO-49/121 precedent). - **Hook anchor:** **`RewardRouterOperations.TryDeliverQuestCompletion`** only — **`QuestStateOperations`** and HTTP quest routes delegate here; no duplicate hooks at quest-state or API layers (NEO-121 engine-only precedent). - **`reward_delivery` timing:** after **`deliveryStore.TryRecord`** returns **`true`** on first-time delivery (fresh grant + store commit). **Not** on early **`TryGet`** idempotent replay (**`already_delivered`**), deny paths, race rollback, or **`SuccessFromEvent`** after a lost **`TryRecord`** race. - **`unlock_granted` stub:** comment-only anchor for future **`UnlockGrant`** apply from bundle rows — prototype Slice 2 bundles are item + skill XP only ([E7M2-01 freeze](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md#prototype-slice-2-freeze-e7m2-01)); no runtime unlock apply loop. Distinct from **`perk_unlock`** (**NEO-49** in **`PerkUnlockEngine`**) which may fire as a side effect of skill XP grants. - **Manual QA doc:** **`docs/manual-qa/NEO-130.md`** — NEO-121/49 comment-only verification pattern. ## Goal, scope, and out-of-scope **Goal:** Document and place **comment-only hook sites** on the server-authoritative quest completion reward path for future E9.M1 catalog events **`reward_delivery`** and **`unlock_granted`**. Align with Epic 7 Slice 2 telemetry vocabulary ([E7.M2 module](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md#related-implementation-slices)). **In scope (from Linear + [E7M2-07](E7M2-prototype-backlog.md#e7m2-07--reward-telemetry-hook-sites-comment-only)):** - **`reward_delivery`** hook site in **`RewardRouterOperations`** (first-time delivery success). - **`unlock_granted`** stub comment (no runtime unlock apply in Slice 2). - **`server/README.md`** reward telemetry subsection. - **`E7_M2_RewardAndUnlockRouter.md`** implementation anchor. - **`docs/manual-qa/NEO-130.md`** — verify comments + no behavior change. - E7M2 backlog + alignment register updates when implementation completes. **Out of scope (from Linear + backlog):** - E9.M1 ingest pipeline; runtime telemetry emit. - **`ILogger`** / metrics / dev-only log lines. - Duplicate hook comments in **`QuestStateOperations`**, **`QuestAcceptApi`**, or **`QuestProgressApi`** (**`quest_complete`** remains NEO-121; **`reward_delivery`** is router-layer grant audit). - Unlock engine / **`UnlockGrant`** runtime apply (pre-production). - Client counterpart (none). ## Acceptance criteria checklist - [x] Hook sites documented in README; no behavior change beyond comments. ## Implementation reconciliation (shipped) - **Engine anchor:** **`RewardRouterOperations.TryDeliverQuestCompletion`** — two comment-only hook sites with **`TODO(E9.M1)`** (no runtime emit); class xml summary cites NEO-130 telemetry anchor. - **`reward_delivery`:** after **`deliveryStore.TryRecord`** returns **`true`** on first-time delivery. - **`unlock_granted`:** stub after item + skill XP apply loops, before delivery event build — future **`UnlockGrant`** apply region; prototype bundles have no unlock rows. - **Docs:** `server/README.md` reward telemetry subsection; E7.M2 module + alignment register + E7M2 backlog updated. - **Manual QA:** `docs/manual-qa/NEO-130.md`. ## Technical approach ### 1. Authoritative surface **`RewardRouterOperations.TryDeliverQuestCompletion`** — all quest completion bundle grants funnel here after NEO-128 wiring. Encounter loot remains on **`EncounterCompletionOperations`** (E5.M3); quest bundles use this router only. ### 2. Hook site A — `reward_delivery` Immediately after **`deliveryStore.TryRecord(deliveryEvent)`** returns **`true`** (first-time store commit): - Comment block names future E9.M1 event **`reward_delivery`**. - **`TODO(E9.M1): catalog emit`** — once per first-time quest completion delivery (idempotent **`TryGet`** replay and race-loser paths excluded). - Planned payload fields from **`RewardDeliveryEvent`**: `playerId`, `questId`, `deliveredAt`, `grantedItems` (`itemId`, `quantity`), `grantedSkillXp` (`skillId`, `amount`), `idempotencyKey` (`{playerId}:quest_complete:{questId}`). ### 3. Hook site B — `unlock_granted` (stub) Comment block after item + skill XP apply loops, **before** building/recording **`RewardDeliveryEvent`** (hypothetical future **`UnlockGrant`** apply region): - Names future E9.M1 event **`unlock_granted`**. - **`TODO(E9.M1): catalog emit`** — once per applied unlock row when bundle unlock grants land (pre-production). - Note: prototype **`completionRewardBundle`** has **no** unlock rows; stub anchors future apply loop only — no runtime behavior in Slice 2. - Planned payload fields (from module **`UnlockGrant`** contract): `playerId`, `questId`, unlock id / kind — finalize at E9.M1 wiring time. ### 4. Cross-references (minimal) - Update **`RewardRouterOperations`** class summary → NEO-130 telemetry hook anchor. - Optional one-line pointer in **`RewardDeliveryEvent.cs`** xml summary → **`reward_delivery`** hook site (mirror **`PerkUnlockEvent`** / NEO-49). - **`server/README.md`:** new **Reward telemetry hooks (NEO-130)** subsection after [Reward router (NEO-127)](../../server/README.md#reward-router-neo-127) with event table. - **`E7_M2_RewardAndUnlockRouter.md`:** NEO-130 landed line under implementation anchor. - **`documentation_and_implementation_alignment.md`** + **`E7M2-prototype-backlog.md`:** E7M2-07 when complete. ### 5. Relationship to adjacent hooks | Event | Layer | Story | |-------|-------|-------| | **`quest_complete`** | **`QuestStateOperations.TryMarkComplete`** after store mark | NEO-121 | | **`reward_delivery`** | **`RewardRouterOperations`** after delivery store record | NEO-130 | | **`perk_unlock`** | **`PerkUnlockEngine.TryUnlockPerks`** when skill XP grants unlock perks | NEO-49 | | **`unlock_granted`** | **`RewardRouterOperations`** stub (future **`UnlockGrant`** apply) | NEO-130 | Deliver-then-mark (NEO-128): **`reward_delivery`** fires **before** **`quest_complete`** in the same completion call stack — expected ordering for E9.M1 correlation. ## Files to add | Path | Purpose | |------|---------| | `docs/manual-qa/NEO-130.md` | Manual QA: verify comment blocks + no behavior/API change. | | `docs/plans/NEO-130-implementation-plan.md` | This plan. | ## Files to modify | Path | Rationale | |------|-----------| | `server/NeonSprawl.Server/Game/Rewards/RewardRouterOperations.cs` | **NEO-130:** comment-only **`reward_delivery`** + **`unlock_granted`** stub hook sites. | | `server/NeonSprawl.Server/Game/Rewards/RewardDeliveryEvent.cs` | Cross-reference **`reward_delivery`** hook anchor in type summary (no behavior change). | | `server/README.md` | Document reward telemetry hook placement and Epic 7 Slice 2 vocabulary (NEO-130). | | `docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md` | Implementation snapshot: NEO-130 telemetry hooks landed. | | `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E7.M2 row: NEO-130 hook sites. | | `docs/plans/E7M2-prototype-backlog.md` | E7M2-07 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 **`RewardRouterOperationsTests`**, quest wiring tests unchanged). Manual verification: **`docs/manual-qa/NEO-130.md`**. | ## Open questions / risks | Question / risk | Agent recommendation | Status | |-----------------|----------------------|--------| | **Duplicate `reward_delivery` vs `quest_complete`** | Keep both — state transition vs grant audit are distinct catalog events; E9.M1 correlates by `playerId` + `questId` + timestamp. | **adopted** | | **`unlock_granted` vs `perk_unlock`** | Stub documents future content **`UnlockGrant`** rows; do not conflate with perk engine side effects from skill XP. | **adopted** | | **Encounter `reward_delivery` TODO (NEO-107)** | Leave E5.M3 producer comments as-is; NEO-130 anchors quest-router consumer path only. | **adopted** |