neon-sprawl/docs/plans/NEO-130-implementation-plan.md

9.2 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 DoneQuestStateOperations.TryMarkCompleteRewardRouterOperations (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, 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); 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).

In scope (from Linear + E7M2-07):

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

Implementation reconciliation (shipped)

  • Engine anchor: RewardRouterOperations.TryDeliverQuestCompletion — two comment-only hook sites with TODO(E9.M1) (no runtime emit).
  • 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) 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