9.8 KiB
NEO-121 — Implementation plan
Story reference
| Field | Value |
|---|---|
| Key | NEO-121 |
| Title | E7M1-10: Quest telemetry hook sites (comment-only) |
| Linear | https://linear.app/neon-sprawl/issue/NEO-121/e7m1-10-quest-telemetry-hook-sites-comment-only |
| Module | E7.M1 — QuestStateMachine · Epic 7 Slice 1 · backlog E7M1-10 |
| Branch | NEO-121-quest-telemetry-hooks |
| Precursor | NEO-117 — QuestStateOperations (landed on main); NEO-118 — QuestObjectiveWiring (landed on main); NEO-119 / NEO-120 — quest progress GET + accept POST (landed on main) |
| Pattern | NEO-64 / NEO-56 / NEO-49 — comment-only engine anchors + TODO(E9.M1); engine-only (no duplicate API-layer hooks) |
| Client counterpart | none (infrastructure-only per E7M1-10) |
Kickoff clarifications
No clarifications needed. E7M1-10 backlog, Linear AC, landed quest operations/wiring (NEO-117/118), and comment-only telemetry precedents (NEO-49/56/64/71) settle scope:
- Runtime behavior: comments-only +
TODO(E9.M1)— no production ingest, noILogger(NEO-49/56 precedent). - Hook anchor:
QuestStateOperationsonly forquest_start,quest_step_complete,quest_complete— HTTP accept POST and objective wiring delegate to the engine; no duplicate API-layer or wiring-layer hooks (NEO-56 engine-only precedent).QuestObjectiveWiringtriggers step advance/complete by callingQuestStateOperations; hooks live at mutation commit sites. quest_starttiming: after successfulTryActivateinTryAccept(first activation only; deny paths excluded).quest_step_completetiming: after successfulTryAdvanceStepstore commit (covers wiring-driven multi-step chains — one emit per committed advance).quest_completetiming: after first-timeTryMarkCompletestore commit; not on idempotent replay when row is alreadycompleted(NEO-117 idempotency + NEO-49 “idempotent reevaluation produces none” precedent).- Optional deny hook: include
quest_accept_deniedcomment inTryAcceptDenyreturns with stablereasonCode(NEO-56inventory_transfer_deniedpattern; backlog lists as optional — adopt for accept funnel parity). - Manual QA doc:
docs/manual-qa/NEO-121.md— NEO-64/49 comment-only verification pattern (no behavior/API change).
Goal, scope, and out-of-scope
Goal: Document and place comment-only hook sites on the server-authoritative quest state path for future E9.M1 catalog events quest_start, quest_step_complete, and quest_complete. Align with Epic 7 Slice 1 telemetry vocabulary (E7_M1 module).
In scope (from Linear + E7M1-10):
- Hook sites in
QuestStateOperationsat accept, step advance, and complete commit paths. - Reserved deny hook
quest_accept_denied(comment-only) onTryAcceptdeny paths. server/README.mdquest telemetry subsection.E7_M1_QuestStateMachine.mdimplementation snapshot note.docs/manual-qa/NEO-121.md— verify comments + no behavior change.- E7M1 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
QuestAcceptApi,QuestProgressApi, orQuestObjectiveWiring(engine-only precedent). - Client HUD (NEO-122).
Acceptance criteria checklist
- Hook sites exist at accept, step complete, quest complete paths.
- Epic 7 Slice 1 telemetry vocabulary documented in
server/README.md.
Implementation reconciliation (shipped)
- Engine anchor:
QuestStateOperations— four comment-only hook sites withTODO(E9.M1)(no runtime emit). quest_start: after successfulTryActivateinTryAccept.quest_step_complete: after successfulTryAdvanceStepstore commit.quest_complete: after first-timeTryMarkCompletecommit (not idempotent replay).quest_accept_denied:DenyAccepthelper on allTryAcceptdeny paths.- Docs:
server/README.mdquest telemetry subsection; E7.M1 module + alignment register + E7M1 backlog updated. - Manual QA:
docs/manual-qa/NEO-121.md.
Technical approach
-
Authoritative surface:
QuestStateOperations— all quest lifecycle mutations funnel here.QuestObjectiveWiringcallsTryAdvanceStep/TryMarkComplete;QuestAcceptApicallsTryAccept. -
Hook site A —
quest_start: InTryAccept, immediately afterprogressStore.TryActivatereturnstrue(before objective wiring refresh):- Comment block names future E9.M1 event
quest_start. TODO(E9.M1): catalog emit(NEO-49 style).- Planned payload fields:
playerId,questId,currentStepIndex(0), optionalschemaVersion.
- Comment block names future E9.M1 event
-
Hook site B —
quest_step_complete: InTryAdvanceStep, immediately afterprogressStore.TryAdvanceStepreturnstrue:- Comment block names future E9.M1 event
quest_step_complete. TODO(E9.M1): catalog emit— emit once per committed step advance (chain quests may emit multiple in one wiring pass).- Planned payload fields:
playerId,questId,completedStepIndex(prior index),newStepIndex, optional step id from catalog at E9.M1 wiring time.
- Comment block names future E9.M1 event
-
Hook site C —
quest_complete: InTryMarkComplete, immediately afterprogressStore.TryMarkCompletereturnstrueon the first-time completion path (after the early idempotentcompletedreturn):- Comment block names future E9.M1 event
quest_complete. TODO(E9.M1): catalog emit— not on idempotent replay.- Planned payload fields:
playerId,questId,completedAt, optional total step count.
- Comment block names future E9.M1 event
-
Hook site D (optional) —
quest_accept_denied: OnTryAcceptdeny returns (centralDenyhelper and/or each deny branch before return):- Comment block names future E9.M1 event
quest_accept_denied. TODO(E9.M1): catalog emitwith stablereasonCode(unknown_quest,prerequisite_incomplete,already_active,already_completed, etc.).- Planned payload fields:
playerId,questId,reasonCode.
- Comment block names future E9.M1 event
-
Cross-references (minimal):
- Update
QuestStateOperationsclass summary → NEO-121 telemetry hook anchor. server/README.md: new Quest telemetry hooks (NEO-121) subsection after quest accept POST (NEO-120).E7_M1_QuestStateMachine.md: NEO-121 landed line under Risks and telemetry.documentation_and_implementation_alignment.md+module_dependency_register.md: E7.M1 row — E7M1-10 when complete.E7M1-prototype-backlog.md: E7M1-10 checkboxes + landed note.
- Update
-
Accept + instant wiring:
TryAcceptmay triggerquest_start, then wiring may emitquest_step_complete/quest_completein the same call stack — order is preserved by code flow (activate → wiring → advance/complete).
Files to add
| Path | Purpose |
|---|---|
docs/manual-qa/NEO-121.md |
Manual QA: verify comment blocks + no behavior/API change. |
docs/plans/NEO-121-implementation-plan.md |
This plan. |
Files to modify
| Path | Rationale |
|---|---|
server/NeonSprawl.Server/Game/Quests/QuestStateOperations.cs |
NEO-121: comment-only quest_start, quest_step_complete, quest_complete, quest_accept_denied hook sites at mutation/deny commit paths. |
server/README.md |
Document quest telemetry hook placement and Epic 7 Slice 1 vocabulary (NEO-121). |
docs/decomposition/modules/E7_M1_QuestStateMachine.md |
Implementation snapshot: NEO-121 telemetry hooks landed. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
E7.M1 row: NEO-121 hook sites. |
docs/decomposition/modules/module_dependency_register.md |
E7.M1 note — E7M1-10 complete when story lands. |
docs/plans/E7M1-prototype-backlog.md |
E7M1-10 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 QuestStateOperationsTests, QuestObjectiveWiringTests, quest HTTP integration tests unchanged). Manual verification: docs/manual-qa/NEO-121.md. |
Open questions / risks
| Question / risk | Agent recommendation | Status |
|---|---|---|
| Duplicate emits on accept + wiring same tick | Expected — quest_start then zero or more quest_step_complete / quest_complete; E9.M1 can correlate by session/time or sample funnels. |
adopted |
quest_accept_denied scope |
Include on all TryAccept deny paths with reasonCode (NEO-56 deny pattern). |
adopted |
| Module Ready vs In Progress | Leave E7.M1 In Progress until client capstones NEO-122/123 land; only note E7M1-10 complete in register. | adopted |