6.2 KiB
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-33–NEO-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,allowedXpSourceKindsper skill. - Bruno request(s) under
bruno/neon-sprawl-server/, mirroring existing collections (e.g.interaction/Get interactables list.bru). docs/manual-qa/NEO-36.mdchecklist (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.mdexists with a short checklist for hitting the endpoint.
Technical approach
-
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 isGET /game/content/skill-definitions—pick one in implementation and keep Bruno/tests aligned; default/game/world/skill-definitionsfor consistency with the other static registry projection. -
Response shape: Follow the interactables pattern: top-level
schemaVersion(const1for first ship of this contract) plus an array (e.g.skills) of objects withid,displayName,category,allowedXpSourceKinds. Order skills byidordinal to matchISkillDefinitionRegistry.GetDefinitionsInIdOrder(). -
Implementation: New static
SkillDefinitionsWorldApi(orSkillCatalogApi) inGame/Skills/that injectsISkillDefinitionRegistry, maps rows to DTOs (either anonymous projection types or small sealed DTO types in a*Dtos.csfile next to the API), returnsResults.Json. Wireapp.MapSkillDefinitionsWorldApi()(name TBD) fromProgram.csnext to otherMap*Apicalls. -
Bruno: New folder
bruno/neon-sprawl-server/skill-definitions/withGet skill definitions.bru(and optionalfolder.bruif the collection uses per-folder metadata likeability-cast). Reuse{{baseUrl}}from environments; tests assert 200, JSON, schema version, array length ≥ 3, and presence of the three frozen ids (mirror style ofGet interactables list.bru). -
Automated tests: Add
SkillDefinitionsWorldApiTests(or equivalent) usingInMemoryWebApplicationFactory,HttpClient.GetAsync,ReadFromJsonAsyncon the response DTO—same structure asInteractablesWorldApiTests, 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 row’s 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
mainalready includes the registry. Ifmainlags a fork, rebase ontomainafter NEO-35 merge. - Path naming: If product prefers
/content/over/world/, swap in one place (route + Bruno + tests) before shipping.