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

7.7 KiB

NEO-39 — Implementation plan

Story reference

Field Value
Key NEO-39
Title Data-driven skill level curve + CI validation
Linear https://linear.app/neon-sprawl/issue/NEO-39/data-driven-skill-level-curve-ci-validation
Module E2.M2 — XpAwardAndLevelEngine
Blocked by NEO-38 (grant path + progression store)

Kickoff clarifications

Topic Question Resolution
Curve shape Which JSON structure should this story ship? User accepted recommendation: use a simple ordered thresholds array (array_thresholds) for level resolution.
Startup failure behavior What if level-curve content is missing/invalid at boot? User accepted recommendation: fail fast at startup; no fallback defaults in this slice.
Scope target Where should curve loading be wired in this story? User accepted recommendation: strict ticket scope; implement only what is required to satisfy NEO-39 acceptance criteria.

Goal, scope, and out-of-scope

Goal: Replace SkillLevelCurvePlaceholder inline threshold logic with data-driven LevelCurve content under content/, validate that content in CI via scripts/validate_content.py, and load the curve at server boot so XP-to-level resolution can be tuned through data without code changes.

In scope (from Linear + kickoff decisions):

  • Add LevelCurve content JSON and JSON Schema using an ordered threshold-array shape.
  • Extend scripts/validate_content.py so CI fails on invalid curve files/schema mismatches.
  • Load and validate level curve content during server startup; fail startup if required curve content is invalid or missing.
  • Switch progression level resolution from SkillLevelCurvePlaceholder constants to loaded curve data.
  • Add tests for curve loading and level boundary behavior.

Out of scope (from Linear):

  • Hot reload / dev file watcher for content.
  • E2.M4 pacing profiles and broader balancing systems.
  • Additional progression API shape changes unrelated to curve source replacement.

Acceptance criteria checklist

  • Curve content + schema exist; CI fails on invalid curve files.
  • Server loads curves at startup; XP/level resolution uses file data (no code change required to tune thresholds).
  • Tests cover curve load and level boundary behavior.

Technical approach

  1. Add a new curve catalog file under content/skills/ with a top-level level-threshold array (prototype-global curve for this slice).
  2. Add a dedicated JSON Schema under content/schemas/ for level-curve rows and enforce strictly increasing XP thresholds.
  3. Extend scripts/validate_content.py to validate both existing skill-def catalogs and the new level-curve catalog(s), failing with clear diagnostics and non-zero exit code on any schema error.
  4. Introduce a server-side level-curve provider/loader that reads validated curve content at boot and registers the resolved thresholds in DI.
  5. Replace SkillLevelCurvePlaceholder.LevelFromTotalXp usage with a resolver backed by loaded content data; remove placeholder-only assumptions from runtime path.
  6. Keep behavior deterministic at boundaries (exact threshold hit levels up; below threshold does not); encode this in tests.
  7. Preserve strict startup behavior: missing or invalid required curve content prevents server startup for prototype safety.

Files to add

Path Purpose
content/skills/prototype_level_curve.json Data source for prototype level thresholds used by runtime level resolution.
content/schemas/level-curve.schema.json JSON Schema for curve file structure and row constraints.
server/NeonSprawl.Server/Game/Skills/ISkillLevelCurve.cs Interface for XP→level resolution from loaded content-backed thresholds.
server/NeonSprawl.Server/Game/Skills/ContentBackedSkillLevelCurve.cs Runtime resolver mapping total XP to level from loaded thresholds.
server/NeonSprawl.Server.Tests/Game/Skills/ContentBackedSkillLevelCurveTests.cs Boundary-focused tests for threshold mapping behavior.

Files to modify

Path Rationale
scripts/validate_content.py Add level-curve catalog/schema validation and CI-fail diagnostics for invalid curve content.
server/NeonSprawl.Server/Game/Skills/SkillProgressionSnapshotApi.cs Ensure progression snapshots resolve level via loaded curve provider instead of placeholder constants.
server/NeonSprawl.Server/Game/Skills/SkillDefinitionCatalogLoader.cs Restrict skill catalog discovery to *_skills.json so level-curve JSON can coexist in content/skills/.
server/db/migrations/V003__player_skill_progression.sql Update comment to reflect data-driven level derivation in runtime.
server/NeonSprawl.Server.Tests/Game/Skills/SkillDefinitionCatalogLoaderTests.cs Update fixture file names to *_skills.json pattern used by loader.
server/NeonSprawl.Server.Tests/Game/Skills/SkillDefinitionRegistryTests.cs Update fixture file name to prototype_skills.json for loader parity.
server/NeonSprawl.Server/Program.cs Register boot-time curve loading and DI wiring for content-backed level resolver.
server/NeonSprawl.Server.Tests/Game/Skills/SkillProgressionGrantApiTests.cs Update/extend assertions to reflect data-driven level thresholds and boundary crossings.
server/README.md Document where level curves live, boot-load/fail-fast behavior, and validation command.
docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md Update implementation snapshot after NEO-39 decisions/landing details.

Tests

Test file What it covers
server/NeonSprawl.Server.Tests/Game/Skills/ContentBackedSkillLevelCurveTests.cs Unit tests for XP threshold boundaries (below first threshold, exact threshold, between thresholds, high-XP upper ranges).
server/NeonSprawl.Server.Tests/Game/Skills/SkillProgressionGrantApiTests.cs Integration tests asserting grant responses use data-driven level resolution and expected levelUps at threshold boundaries.
server/NeonSprawl.Server.Tests/Game/Skills/SkillProgressionSnapshotApiTests.cs Snapshot endpoint reflects data-driven levels (including unchanged level below next threshold).
scripts/validate_content.py (invoked in CI) Validation failure behavior for malformed level-curve content/schema mismatch.

If script-level tests are not present in this repository, verification for validator changes will be covered by running the validation script against good and intentionally bad fixtures during implementation and documenting results in the plan Decisions section.

Decisions

  • Adopted *_skills.json and *_level_curve.json file naming split under content/skills/ so both catalogs can coexist without ambiguous loader behavior.
  • Kept startup fail-fast for level-curve load: missing/invalid content/skills/prototype_level_curve.json or missing schema now throws during boot.
  • Verification outcomes:
    • python3 scripts/validate_content.py passes with skill + level-curve catalogs.
    • dotnet test server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --no-restore passes (132 tests).

Open questions / risks

  • Schema strictness trade-off: very strict schema constraints reduce bad data risk but can make iterative tuning slower; keep constraints targeted to structural correctness and monotonic thresholds.
  • Boot coupling: fail-fast startup improves safety but increases sensitivity to content mistakes; error messages in validator/boot path must be precise.
  • None otherwise.