From effd234ee3ef785eb49e1f8035d54807b164f142 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 10 May 2026 18:49:40 -0400 Subject: [PATCH] NEO-40: add progression telemetry hook comment sites --- .../Post skill progression grant.bru | 1 + .../modules/E2_M2_XpAwardAndLevelEngine.md | 4 ++- docs/manual-qa/NEO-40.md | 32 +++++++++++++++++++ docs/plans/NEO-40-implementation-plan.md | 8 ++--- .../Skills/SkillProgressionGrantApiTests.cs | 1 + .../Skills/SkillProgressionSnapshotApi.cs | 9 ++++++ 6 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 docs/manual-qa/NEO-40.md diff --git a/bruno/neon-sprawl-server/skill-progression/Post skill progression grant.bru b/bruno/neon-sprawl-server/skill-progression/Post skill progression grant.bru index 8bfa23d..355e773 100644 --- a/bruno/neon-sprawl-server/skill-progression/Post skill progression grant.bru +++ b/bruno/neon-sprawl-server/skill-progression/Post skill progression grant.bru @@ -6,6 +6,7 @@ meta { docs { NEO-38: single skill XP grant + allowlist + level-up metadata; see server README. + NEO-40: same contract; server adds comment-only telemetry hook markers (no request/response change). } post { diff --git a/docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md b/docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md index 36552b2..781f28d 100644 --- a/docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md +++ b/docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md @@ -19,7 +19,9 @@ **NEO-39 landed:** data-driven **`LevelCurve`** at `content/skills/prototype_level_curve.json` with schema `content/schemas/level-curve.schema.json`; CI validation extended in `scripts/validate_content.py`; server boot now fail-fast loads curve content and uses it for XP→level resolution in skill progression GET/POST paths (no code edit required for threshold tuning). -**Still backlog within this module:** telemetry hook sites (**NEO-40**); callers in Slice 3 (**NEO-41**–**NEO-43**). Keep this paragraph and [documentation tracking](documentation_and_implementation_alignment.md) in sync as Slice 2 stories merge. +**NEO-40 landed:** comment-only **telemetry hook sites** in `SkillProgressionSnapshotApi` (`POST …/skill-progression`) for future catalog events **`xp_grant`** and **`level_up`** ([implementation plan](../../plans/NEO-40-implementation-plan.md)); manual QA **[`NEO-40.md`](../../manual-qa/NEO-40.md)**. + +**Still backlog within this module:** callers in Slice 3 (**NEO-41**–**NEO-43**). Keep this paragraph and [documentation tracking](documentation_and_implementation_alignment.md) in sync as Slice 2 stories merge. ## Purpose diff --git a/docs/manual-qa/NEO-40.md b/docs/manual-qa/NEO-40.md new file mode 100644 index 0000000..c00365e --- /dev/null +++ b/docs/manual-qa/NEO-40.md @@ -0,0 +1,32 @@ +# Manual QA — NEO-40 (progression telemetry hook sites) + +Reference: [implementation plan](../plans/NEO-40-implementation-plan.md), [NEO-38 manual QA](./NEO-38.md) (same server surface). + +## Preconditions + +- Same as NEO-38: run `NeonSprawl.Server` (`dotnet run` from `server/NeonSprawl.Server`; default `http://localhost:5253`). +- This story adds **comments only**; behavior matches NEO-38. + +```bash +BASE=http://localhost:5253 +ID=dev-local-1 +``` + +## Verify API unchanged + +1. **Grant** (same as NEO-38 happy path): + +```bash +curl -sS -i -X POST "${BASE}/game/players/${ID}/skill-progression" \ + -H "Content-Type: application/json" \ + -d '{"schemaVersion":1,"skillId":"salvage","amount":10,"sourceKind":"activity"}' +``` + +Expect **HTTP 200**, `granted` **true**, `progression` and `levelUps` present as before. + +2. **Level-up path** (optional, same as NEO-38 bonus): large grant on `intrusion` if XP crosses the curve threshold; expect `levelUps` non-empty when a boundary is crossed. + +## Code review check + +- Open `server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs` and confirm **comment blocks** mark **`xp_grant`** (after successful `TryApplyXpDelta`) and **`level_up`** (inside `nextLevel > prevLevel`), referencing **E9.M1** / no production ingest. +- Confirm there are **no** new `ILogger` or metrics calls for these events. diff --git a/docs/plans/NEO-40-implementation-plan.md b/docs/plans/NEO-40-implementation-plan.md index 4067678..3d58f26 100644 --- a/docs/plans/NEO-40-implementation-plan.md +++ b/docs/plans/NEO-40-implementation-plan.md @@ -34,8 +34,8 @@ ## Acceptance criteria checklist -- [ ] Hook locations in **grant** + **level-up** path; documented here **and** in code comments referencing future **`xp_grant`** / **`level_up`** (and **time-to-first-level-up** intent where appropriate). -- [ ] **No** mistaken high-volume logging to production pipelines (no new log statements for these hooks). +- [x] Hook locations in **grant** + **level-up** path; documented here **and** in code comments referencing future **`xp_grant`** / **`level_up`** (and **time-to-first-level-up** intent where appropriate). +- [x] **No** mistaken high-volume logging to production pipelines (no new log statements for these hooks). ## Technical approach @@ -53,14 +53,14 @@ | Path | Purpose | |------|---------| -| **none** | Hook sites are **comment markers** on the existing NEO-38 route; no new types or files required. | +| `docs/manual-qa/NEO-40.md` | Manual QA checklist (API parity + comment review). | ## Files to modify | Path | Rationale | |------|-----------| | `server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs` | Insert **telemetry hook site** comments on the **successful grant** path (`xp_grant`) and **level-up** branch (`level_up`); optional one-line **time-to-first-level-up** intent. | -| `docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md` | **Only if** a single sentence improves traceability from module doc → hook file; otherwise **none** (YAGNI). | +| `docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md` | **NEO-40 landed** line + backlog sentence updated (replaces “telemetry still backlog”). | ## Tests diff --git a/server/NeonSprawl.Server.Tests/Game/Skills/SkillProgressionGrantApiTests.cs b/server/NeonSprawl.Server.Tests/Game/Skills/SkillProgressionGrantApiTests.cs index bd8f174..c6695e0 100644 --- a/server/NeonSprawl.Server.Tests/Game/Skills/SkillProgressionGrantApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Skills/SkillProgressionGrantApiTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace NeonSprawl.Server.Tests.Game.Skills; +/// Regression tests for POST …/skill-progression (NEO-38). NEO-40 adds comment-only hook sites; JSON contract unchanged. public sealed class SkillProgressionGrantApiTests { private static SkillProgressionGrantRequest Grant( diff --git a/server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs b/server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs index 0798e8c..598fc63 100644 --- a/server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs +++ b/server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs @@ -81,11 +81,20 @@ public static class SkillProgressionSnapshotApi return Results.NotFound(); } + // --- Telemetry hook site (NEO-40): future E9.M1 catalog event `xp_grant` --- + // No ingest here until the telemetry catalog exists. Planned payload fields: playerId, + // skillId, amount, sourceKind; optional correlation/request ids when Slice 3 wires callers. + var prevLevel = levelCurve.LevelFromTotalXp(prevXp); var nextLevel = levelCurve.LevelFromTotalXp(newXp); var levelUps = new List(); if (nextLevel > prevLevel) { + // --- Telemetry hook site (NEO-40): future E9.M1 catalog event `level_up` --- + // Planned fields: playerId, skillId, previousLevel, newLevel. No logging (comments-only). + // Time-to-first-level-up: needs first-seen / milestone persistence not present here; wire + // when catalog + storage exist (likely derived from `level_up` or a dedicated milestone). + levelUps.Add( new SkillLevelUpJson {