7.7 KiB
NEO-56 — Implementation plan
Story reference
| Field | Value |
|---|---|
| Key | NEO-56 |
| Title | E3.M3: item_created / transfer-failure telemetry hook sites |
| Linear | https://linear.app/neon-sprawl/issue/NEO-56/e3m3-item-created-transfer-failure-telemetry-hook-sites |
| Module | E3.M3 — ItemizationAndInventorySchema · Epic 3 Slice 1 (E3M3-07) |
| Branch | NEO-56-item-created-transfer-failure-telemetry-hooks |
| Precursors | NEO-54 — PlayerInventoryOperations (Done); NEO-55 — inventory GET/POST HTTP (Done) |
| Pattern | NEO-49 / NEO-40 — comment-only engine anchors; NEO-30 — deny event + reasonCode |
Kickoff clarifications
| Topic | Question | Agent recommendation | Answer |
|---|---|---|---|
| Hook anchor | Engine only vs engine + HTTP? | Engine only — PlayerInventoryOperations (NEO-49 single-anchor precedent; NEO-55 deferred hooks here). |
User: engine only. |
| Deny event shape | Per-reason events vs single deny + reasonCode? |
inventory_transfer_denied + reasonCode (NEO-30 ability_cast_denied; epic inventory_full maps to a reason code). |
User: single deny + reasonCode. |
item_created scope |
Add success only vs add + remove? | Successful add only (TryAddStack → Applied). |
User: add success only. |
| Runtime behavior | Comments-only vs dev ILogger? |
Comments-only — TODO(E9.M1); no production logging. |
User: comments-only. |
| Manual QA doc | docs/manual-qa/NEO-56.md? |
Yes — NEO-49 verification pattern. | User: yes. |
Goal, scope, and out-of-scope
Goal: Document and place comment-only hook sites on the server-authoritative inventory mutation path for future E9.M1 catalog events item_created (successful add) and inventory_transfer_denied (transfer failures with stable reasonCode). Align with Epic 3 Slice 1 telemetry vocabulary (epic_03 — Slice 1).
In scope (from Linear + E3M3-07):
- Hook placement in
PlayerInventoryOperations(TryAddStack/TryRemoveStack) withTODO(E9.M1)comments. server/README.mdinventory subsection + E3_M3 module hook note.docs/manual-qa/NEO-56.md— verify comments + no behavior/API change.
Out of scope (from Linear):
- Telemetry ingest, dashboards (E9.M1).
ILogger/ metrics / dev-only log lines.- Duplicate hook comments in
PlayerInventoryApi(HTTP calls engine; NEO-55 plan deferred hooks to NEO-56). item_createdon successful remove (kickoff decision).StoreMissinghooks (not a transfer failure — HTTP maps to 404).
Acceptance criteria checklist
- Hook sites documented in code (
PlayerInventoryOperations) withTODO(E9.M1). item_createdon successful add;inventory_transfer_denied+reasonCodeon rule denies (including early validation denies).server/README.mddocuments hook placement.- Matches Epic 3 Slice 1 vocabulary (
item_created,inventory_full/ transfer failures viareasonCode).
Technical approach
-
Authoritative surface:
PlayerInventoryOperations— all inventory add/remove rules and outcomes. Callers (PlayerInventoryApiPOST, future gather/craft) funnel here; no duplicate markers at HTTP. -
Hook site A —
item_created: InTryAddStack, immediately before returningPlayerInventoryMutationKind.Applied(after successfulTryMutateSnapshotwith nodenyReason):- Comment block names future E9.M1 event
item_created. TODO(E9.M1): catalog emit(NEO-49 / NEO-30 style).- Planned payload fields:
playerId,itemId,quantity, optionalmutationKind/sourcewhen Slice 2+ callers exist. - Not on
TryRemoveStacksuccess (kickoff).
- Comment block names future E9.M1 event
-
Hook site B —
inventory_transfer_denied: On everyDeniedreturn path inTryAddStackandTryRemoveStack:- Early
Deny(...)(e.g.invalid_quantity,invalid_itembefore store write). - Post-mutation deny when
denyReasonis set (inventory_full,insufficient_quantity). - Single comment pattern: catalog event
inventory_transfer_denied+reasonCodefromPlayerInventoryReasonCodes(inventory_full,invalid_item,insufficient_quantity,invalid_quantity). - Epic Slice 1
inventory_fullis the flagship transfer-failure code; others share the same deny event shape (NEO-30 precedent).
- Early
-
No hook on
StoreMissing: Outcome is missing player/store — not an inventory transfer rule deny. -
Cross-references (minimal):
- One-line pointer in
PlayerInventoryMutationOutcome.csorPlayerInventoryReasonCodes.csxml summary → hook sites inPlayerInventoryOperations. server/README.md: extend Player inventory (NEO-54 store, NEO-55 HTTP) with NEO-56 telemetry hooks subsection (anchor file + event names).E3_M3_ItemizationAndInventorySchema.md: NEO-56 landed line under Related implementation slices.documentation_and_implementation_alignment.md: E3.M3 row cites NEO-56 hook sites (still no E9.M1 ingest).
- One-line pointer in
-
Regression: Run
dotnet teston server solution; existingPlayerInventoryOperationsTests/PlayerInventoryApiTestsunchanged (comments-only).
Files to add
| Path | Purpose |
|---|---|
docs/manual-qa/NEO-56.md |
Manual QA: verify hook comment blocks + no API/behavior change. |
Files to modify
| Path | Rationale |
|---|---|
server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs |
NEO-56: item_created on add Applied; inventory_transfer_denied on all Denied paths in add/remove. |
server/NeonSprawl.Server/Game/Items/PlayerInventoryReasonCodes.cs |
Cross-reference deny reasonCode values → transfer-denied hook anchor (no behavior change). |
server/README.md |
Document inventory telemetry hook placement (NEO-56). |
docs/decomposition/modules/E3_M3_ItemizationAndInventorySchema.md |
Implementation snapshot: NEO-56 telemetry hooks. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
E3.M3 row: NEO-56 hook sites. |
Tests
| Path | Change |
|---|---|
| None required | Comments-only; existing PlayerInventoryOperationsTests and PlayerInventoryApiTests cover add/remove/deny paths. |
Manual verification: docs/manual-qa/NEO-56.md — confirm item_created and inventory_transfer_denied comments, TODO(E9.M1), payload field notes, no new log statements; dotnet test regression.
Open questions / risks
| Question / risk | Agent recommendation | Status |
|---|---|---|
| Future gather/craft callers | Hooks stay in engine; callers pass source/mutationKind in E9.M1 payload when catalog exists — document in hook comments as optional fields. |
adopted |
| HTTP request correlation | Defer requestId to E9.M1 / future sink; do not add API-layer hooks (kickoff: engine only). |
adopted |
| Partial stack placement on add | NEO-54 all-or-nothing add — single item_created emit for full requested quantity on Applied. |
adopted |
None blocking.