9.6 KiB
NEO-78 — Implementation plan
Story reference
| Field | Value |
|---|---|
| Key | NEO-78 |
| Title | E5M1-04: GET /game/world/ability-definitions |
| Linear | https://linear.app/neon-sprawl/issue/NEO-78/e5m1-04-get-gameworldability-definitions |
| Module | E5.M1 — CombatRulesEngine · Epic 5 Slice 1 · backlog E5M1-04 |
| Branch | NEO-78-get-world-ability-definitions |
| Precursor | NEO-79 — IAbilityDefinitionRegistry + DI (Done on main) |
| Pattern | NEO-68 — GET /game/world/recipe-definitions; NEO-53 — item definitions world GET |
| Client counterpart | None (server-only); optional client enrichment in NEO-85 via this HTTP route |
Kickoff clarifications
| Topic | Question | Agent recommendation | Answer |
|---|---|---|---|
| Manual QA doc | Add docs/manual-qa/NEO-78.md? |
Yes — NEO-68 pattern for world GET HTTP. | User: skip — manual QA only when there are client-facing changes; this story is server-only (Bruno + automated tests suffice). |
Optional abilityKind |
Emit when row has no kind? | Omit property when null — NEO-68 “loaded fields only”; all four prototype rows have a kind today. | User: omit when null. |
No other blocking decisions — route path, envelope (schemaVersion 1, abilities array), row fields, registry injection, and frozen four-id order are specified in E5M1-04 and E5.M1 freeze box.
Goal, scope, and out-of-scope
Goal: Expose a stable, read-only JSON endpoint that returns all four loaded prototype ability definitions for Bruno, tooling, and optional client preview (NEO-85) — backed by IAbilityDefinitionRegistry without duplicating catalog truth.
In scope (from Linear + E5M1-04):
GET /game/world/ability-definitionswith versioned envelope (schemaVersion1,abilitiesarray).- Row fields:
id,displayName,baseDamage,cooldownSeconds,abilityKind(omit when null). - Abilities ordered by
idordinal, matchingGetDefinitionsInIdOrder(). - Bruno folder
bruno/neon-sprawl-server/ability-definitions/. server/README.mdsection.- API integration tests (AAA).
Out of scope (from Linear + backlog):
- Godot HUD / client wiring (NEO-85).
- Combat resolution, damage application, cooldown commit (NEO-80+).
- Per-player ability state or loadout mutation.
docs/manual-qa/NEO-78.md(server-only; no client-facing changes per kickoff).
Acceptance criteria checklist
- GET returns all four prototype abilities with
schemaVersion1 and stable field names (id,displayName,baseDamage,cooldownSeconds,abilityKindwhen present). - Bruno happy path documents response shape and runs against dev server.
- Automated API tests (AAA) assert four ids in registry order and spot-check metadata.
Technical approach
-
Route:
GET /game/world/ability-definitions— parameterless read; injectIAbilityDefinitionRegistryonly (notAbilityDefinitionCatalog). -
Response shape: Top-level
schemaVersion(AbilityDefinitionsListResponse.CurrentSchemaVersion= 1) plusabilitiesarray. Each row maps fromAbilityDefRow:id,displayName,baseDamage,cooldownSeconds,abilityKind. UseJsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)onabilityKindso absent kinds are omitted from JSON. -
Ordering: Build list from
registry.GetDefinitionsInIdOrder()(already ordinal by id). Tests assert exact id sequence (frozen four from E5.M1 freeze box):prototype_burst,prototype_dash,prototype_guard,prototype_pulse(ordinal sort of the four ids — same order as
AbilityDefinitionRegistryTests/ catalog keys). -
Implementation: New
AbilityDefinitionsWorldApi+AbilityDefinitionsListDtos.csinGame/Combat/(mirrorRecipeDefinitionsWorldApi). Wireapp.MapAbilityDefinitionsWorldApi()fromProgram.csnext to other world definition APIs (afterMapRecipeDefinitionsWorldApi()). -
Bruno:
bruno/neon-sprawl-server/ability-definitions/withGet ability definitions.bru+folder.bru. Tests: 200, JSON,schemaVersion1,abilities.length === 4, ascending id order, exact frozen-four registry sequence, spot-checkprototype_pulse(baseDamage25,cooldownSeconds3.0,abilityKindattack) andprototype_burst(baseDamage40,cooldownSeconds5.0,abilityKindattack). -
Docs: Update
server/README.mdability-catalog section with GET + curl example. Update E5_M1 Related implementation slices and documentation_and_implementation_alignment.md E5.M1 row when landed.
Expected prototype row values (from content)
id |
displayName |
baseDamage |
cooldownSeconds |
abilityKind |
|---|---|---|---|---|
prototype_pulse |
Prototype Pulse | 25 | 3.0 | attack |
prototype_guard |
Prototype Guard | 0 | 6.0 | utility |
prototype_dash |
Prototype Dash | 0 | 4.0 | movement |
prototype_burst |
Prototype Burst | 40 | 5.0 | attack |
Files to add
| Path | Purpose |
|---|---|
server/NeonSprawl.Server/Game/Combat/AbilityDefinitionsWorldApi.cs |
Map* extension registering GET route; maps registry → JSON. |
server/NeonSprawl.Server/Game/Combat/AbilityDefinitionsListDtos.cs |
Versioned response + row DTOs for JSON serialization. |
server/NeonSprawl.Server.Tests/Game/Combat/AbilityDefinitionsWorldApiTests.cs |
HTTP integration: 200, schema v1, four ids in order, spot-check rows. |
bruno/neon-sprawl-server/ability-definitions/Get ability definitions.bru |
Manual / Bruno verification against dev server. |
bruno/neon-sprawl-server/ability-definitions/folder.bru |
Bruno folder metadata (match recipe-definitions). |
docs/plans/NEO-78-implementation-plan.md |
This plan. |
Files to modify
| Path | Rationale |
|---|---|
server/NeonSprawl.Server/Program.cs |
Register MapAbilityDefinitionsWorldApi() alongside other game world definition APIs. |
server/README.md |
Document GET /game/world/ability-definitions, curl example, links to plan/Bruno. |
docs/decomposition/modules/E5_M1_CombatRulesEngine.md |
Related implementation slices — HTTP read model bullet (NEO-78). |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
E5.M1 row — note NEO-78 HTTP projection when landed. |
Tests
| File | Coverage |
|---|---|
server/NeonSprawl.Server.Tests/Game/Combat/AbilityDefinitionsWorldApiTests.cs |
Integration: GET /game/world/ability-definitions via InMemoryWebApplicationFactory; ReadFromJsonAsync; assert schemaVersion 1; abilities count 4; ordered id list equals frozen four in ordinal id order; spot-check prototype_pulse and prototype_burst damage/cooldown/kind. AAA per csharp-style. |
Bruno scripts complement automated tests for manual dev-server verification. No docs/manual-qa/NEO-78.md (server-only per kickoff).
Open questions / risks
| Question / risk | Agent recommendation | Status |
|---|---|---|
| Linear blockedBy NEO-79 | Branch from main where NEO-79 is merged (confirmed at kickoff; branch fast-forwarded). |
adopted |
| Optional fields later | When content adds new optional ability-def fields, bump schemaVersion or extend v1 in a follow-up issue. |
deferred |
| Client not in this story | Cross-link NEO-85 in plan; Bruno is not prototype-complete per full-stack epic decomposition. | adopted |
cooldownSeconds JSON type |
Emit as JSON number (double) matching content schema; tests assert numeric equality. | adopted |
Decisions (kickoff)
- No manual QA doc — server-only HTTP; Bruno + automated tests (user confirmed).
abilityKindomitted when null — v1 contract emits loaded fields only (user confirmed).- Registry-only injection — HTTP layer depends on
IAbilityDefinitionRegistry, notAbilityDefinitionCatalog.
Reconciliation (implementation)
AbilityDefinitionsWorldApi+AbilityDefinitionsListDtosregistered viaMapAbilityDefinitionsWorldApi()inProgram.cs; injectsIAbilityDefinitionRegistryonly.AbilityDefinitionsWorldApiTests— integration test for schema v1, frozen-four id order,prototype_pulse/prototype_burstspot-checks.- Bruno:
bruno/neon-sprawl-server/ability-definitions/Get ability definitions.bruwith response-shape tests. server/README.md, E5.M1 module doc, alignment register, and E5M1 backlog updated.