From 7493a6b078a90db721c03874dad54a3483cd0233 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 7 Jun 2026 22:45:12 -0400 Subject: [PATCH] NEO-130: Add kickoff implementation plan for reward telemetry hooks. --- docs/plans/NEO-130-implementation-plan.md | 123 ++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 docs/plans/NEO-130-implementation-plan.md diff --git a/docs/plans/NEO-130-implementation-plan.md b/docs/plans/NEO-130-implementation-plan.md new file mode 100644 index 0000000..b5abafc --- /dev/null +++ b/docs/plans/NEO-130-implementation-plan.md @@ -0,0 +1,123 @@ +# 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 + +- [ ] Hook sites documented in README; no behavior change beyond comments. + +## 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** |