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

6.2 KiB
Raw Blame History

NEO-36 — Implementation plan

Story reference

Field Value
Key NEO-36
Title E2.M1: Read-only skill catalog HTTP projection + Bruno
Linear https://linear.app/neon-sprawl/issue/NEO-36/e2m1-read-only-skill-catalog-http-projection-bruno
Module E2.M1 — SkillDefinitionRegistry

Kickoff clarifications

No questions were put to the user during kickoff.

Reason: The issue specifies the response fields (id, displayName, category, allowedXpSourceKinds), excludes mutations and client polish, and points at “match server routing style.” The repo already exposes a read-only world projection at GET /game/world/interactables (InteractablesWorldApi) with a versioned JSON envelope—this story can mirror that pattern. ISkillDefinitionRegistry and the frozen prototype trio (salvage, refine, intrusion) are present on main from NEO-33NEO-35 (prototype_skills.json, SkillDefinitionRegistryTests). Linear may still list blockedBy NEO-35; the implementation branch is based on main where that work is already merged.

If you want a different public path than the default in Technical approach (below), say so before implementation.

Goal, scope, and out-of-scope

Goal: Expose a stable, read-only JSON endpoint that returns all loaded prototype skill definitions for dev tooling, Bruno, and manual QA, backed by ISkillDefinitionRegistry without duplicating catalog truth outside the server.

In scope (from Linear):

  • One GET route consistent with existing APIs (under /game/, same style as other read-only projections).
  • JSON rows including id, displayName, category, allowedXpSourceKinds per skill.
  • Bruno request(s) under bruno/neon-sprawl-server/, mirroring existing collections (e.g. interaction/Get interactables list.bru).
  • docs/manual-qa/NEO-36.md checklist (endpoint is user-visible for QA per repo rules).

Out of scope (from Linear):

  • Mutations or authoring APIs.
  • Client UX / debug panel (optional and not required).

Acceptance criteria checklist

  • Bruno (and/or automated test) can call the endpoint against a running dev server and observe the three frozen ids (salvage, refine, intrusion).
  • docs/manual-qa/NEO-36.md exists with a short checklist for hitting the endpoint.

Technical approach

  1. Route: Add GET /game/world/skill-definitions (parallel to /game/world/interactables) as a parameterless read. If “world” reads odd for catalog-only data, acceptable alternative is GET /game/content/skill-definitions—pick one in implementation and keep Bruno/tests aligned; default /game/world/skill-definitions for consistency with the other static registry projection.

  2. Response shape: Follow the interactables pattern: top-level schemaVersion (const 1 for first ship of this contract) plus an array (e.g. skills) of objects with id, displayName, category, allowedXpSourceKinds. Order skills by id ordinal to match ISkillDefinitionRegistry.GetDefinitionsInIdOrder().

  3. Implementation: New static SkillDefinitionsWorldApi (or SkillCatalogApi) in Game/Skills/ that injects ISkillDefinitionRegistry, maps rows to DTOs (either anonymous projection types or small sealed DTO types in a *Dtos.cs file next to the API), returns Results.Json. Wire app.MapSkillDefinitionsWorldApi() (name TBD) from Program.cs next to other Map*Api calls.

  4. Bruno: New folder bruno/neon-sprawl-server/skill-definitions/ with Get skill definitions.bru (and optional folder.bru if the collection uses per-folder metadata like ability-cast). Reuse {{baseUrl}} from environments; tests assert 200, JSON, schema version, array length ≥ 3, and presence of the three frozen ids (mirror style of Get interactables list.bru).

  5. Automated tests: Add SkillDefinitionsWorldApiTests (or equivalent) using InMemoryWebApplicationFactory, HttpClient.GetAsync, ReadFromJsonAsync on the response DTO—same structure as InteractablesWorldApiTests, asserting the trio and ordering.

Files to add

Path Purpose
server/NeonSprawl.Server/Game/Skills/SkillDefinitionsWorldApi.cs (or chosen name) Map* extension registering GET route; maps registry → JSON.
server/NeonSprawl.Server/Game/Skills/SkillDefinitionsListDtos.cs (or inline records in API file if kept tiny) Versioned response + row DTOs for JSON serialization.
server/NeonSprawl.Server.Tests/Game/Skills/SkillDefinitionsWorldApiTests.cs HTTP integration: 200, schema, three ids, id order.
bruno/neon-sprawl-server/skill-definitions/Get skill definitions.bru Manual / Bruno verification against dev server.
bruno/neon-sprawl-server/skill-definitions/folder.bru Optional; match Bruno folder conventions used elsewhere.
docs/manual-qa/NEO-36.md Checklist: start server, call GET, confirm trio (and note Bruno path).

Files to modify

Path Rationale
server/NeonSprawl.Server/Program.cs Register the new Map*Api() alongside existing game APIs.

Tests

Test file What it covers
server/NeonSprawl.Server.Tests/Game/Skills/SkillDefinitionsWorldApiTests.cs Integration: GET new route via InMemoryWebApplicationFactory; ReadFromJsonAsync; assert schemaVersion, ordered id list equals intrusion, refine, salvage (ordinal sort); spot-check one rows allowedXpSourceKinds. AAA comments per csharp-style.

Bruno scripts are manual verification; they complement but do not replace the automated test above.

Open questions / risks

  • Linear relation: Issue may still show blockedBy NEO-35; code on main already includes the registry. If main lags a fork, rebase onto main after NEO-35 merge.
  • Path naming: If product prefers /content/ over /world/, swap in one place (route + Bruno + tests) before shipping.