9.3 KiB
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 · Epic 7 Slice 2 · backlog E7M2-07 |
| Branch | NEO-130-reward-telemetry-hooks |
| Precursor | NEO-128 Done — QuestStateOperations.TryMarkComplete → RewardRouterOperations (deliver-then-mark) |
| Pattern | NEO-121 / NEO-49 — comment-only engine anchors + TODO(E9.M1); engine-only (no duplicate API-layer hooks) |
| Client counterpart | none (infrastructure-only per E7M2-07) |
Kickoff clarifications
No clarifications needed. E7M2-07 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, noILogger(NEO-49/121 precedent). - Hook anchor:
RewardRouterOperations.TryDeliverQuestCompletiononly —QuestStateOperationsand HTTP quest routes delegate here; no duplicate hooks at quest-state or API layers (NEO-121 engine-only precedent). reward_deliverytiming: afterdeliveryStore.TryRecordreturnstrueon first-time delivery (fresh grant + store commit). Not on earlyTryGetidempotent replay (already_delivered), deny paths, race rollback, orSuccessFromEventafter a lostTryRecordrace.unlock_grantedstub: comment-only anchor for futureUnlockGrantapply from bundle rows — prototype Slice 2 bundles are item + skill XP only (E7M2-01 freeze); no runtime unlock apply loop. Distinct fromperk_unlock(NEO-49 inPerkUnlockEngine) 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).
In scope (from Linear + E7M2-07):
reward_deliveryhook site inRewardRouterOperations(first-time delivery success).unlock_grantedstub comment (no runtime unlock apply in Slice 2).server/README.mdreward telemetry subsection.E7_M2_RewardAndUnlockRouter.mdimplementation 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, orQuestProgressApi(quest_completeremains NEO-121;reward_deliveryis router-layer grant audit). - Unlock engine /
UnlockGrantruntime apply (pre-production). - Client counterpart (none).
Acceptance criteria checklist
- Hook sites documented in README; no behavior change beyond comments.
Implementation reconciliation (shipped)
- Engine anchor:
RewardRouterOperations.TryDeliverQuestCompletion— two comment-only hook sites withTODO(E9.M1)(no runtime emit); class xml summary cites NEO-130 telemetry anchor. reward_delivery: afterdeliveryStore.TryRecordreturnstrueon first-time delivery.unlock_granted: stub after item + skill XP apply loops, before delivery event build — futureUnlockGrantapply region; prototype bundles have no unlock rows.- Docs:
server/README.mdreward 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 (idempotentTryGetreplay 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
completionRewardBundlehas no unlock rows; stub anchors future apply loop only — no runtime behavior in Slice 2. - Planned payload fields (from module
UnlockGrantcontract):playerId,questId, unlock id / kind — finalize at E9.M1 wiring time.
4. Cross-references (minimal)
- Update
RewardRouterOperationsclass summary → NEO-130 telemetry hook anchor. - Optional one-line pointer in
RewardDeliveryEvent.csxml summary →reward_deliveryhook site (mirrorPerkUnlockEvent/ NEO-49). server/README.md: new Reward telemetry hooks (NEO-130) subsection after 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 |