# NEO-90 — Implementation plan ## Story reference | Field | Value | |--------|--------| | **Key** | NEO-90 | | **Title** | E5M2-04: GET `/game/world/npc-behavior-definitions` | | **Linear** | https://linear.app/neon-sprawl/issue/NEO-90/e5m2-04-get-gameworldnpc-behavior-definitions | | **Module** | [E5.M2 — NpcAiAndBehaviorProfiles](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) · Epic 5 Slice 2 · backlog **E5M2-04** | | **Branch** | `NEO-90-npc-behavior-definitions-get` | | **Precursor** | [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) — `INpcBehaviorDefinitionRegistry` + DI (**Done** on `main`) | | **Pattern** | [NEO-78](https://linear.app/neon-sprawl/issue/NEO-78) — `GET /game/world/ability-definitions`; [NEO-68](https://linear.app/neon-sprawl/issue/NEO-68) — recipe definitions world GET | | **Client counterpart** | None (server-only); player-visible labels/HUD land in [NEO-97](https://linear.app/neon-sprawl/issue/NEO-97) / capstone [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) | ## Kickoff clarifications | Topic | Question | Agent recommendation | Answer | |--------|----------|----------------------|--------| | **Response array key** | Top-level JSON array name? | **`npcBehaviors`** — matches `content/npc-behaviors/*.json` catalog envelope. | **User:** `npcBehaviors`. | | **Manual QA doc** | Add `docs/manual-qa/NEO-90.md`? | **Skip** — server-only HTTP; Bruno + integration tests (NEO-78/89 pattern). | **User:** skip. | No other blocking decisions — route path, `schemaVersion` **1**, registry injection, row fields (all required schema fields), and frozen three-id order are specified in [E5M2-04](E5M2-prototype-backlog.md#e5m2-04--get-gameworldnpc-behavior-definitions) and the [E5.M2 freeze box](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01). ## Goal, scope, and out-of-scope **Goal:** Expose a stable, read-only JSON endpoint that returns all three loaded prototype NPC behavior definitions for Bruno, tooling, and future client labels ([NEO-97](https://linear.app/neon-sprawl/issue/NEO-97)) — backed by **`INpcBehaviorDefinitionRegistry`** without duplicating catalog truth. **In scope (from Linear + [E5M2-04](E5M2-prototype-backlog.md#e5m2-04--get-gameworldnpc-behavior-definitions)):** - **`GET /game/world/npc-behavior-definitions`** with versioned envelope (`schemaVersion` **1**, **`npcBehaviors`** array). - Row fields: **`id`**, **`displayName`**, **`archetypeKind`**, **`maxHp`**, **`aggroRadius`**, **`leashRadius`**, **`telegraphWindupSeconds`**, **`attackDamage`**, **`attackCooldownSeconds`** (all required in v1; no optional omissions). - Behaviors ordered by **`id`** ordinal, matching **`GetDefinitionsInIdOrder()`**. - Bruno folder `bruno/neon-sprawl-server/npc-behavior-definitions/`. - `server/README.md` route section. - API integration tests (AAA). **Out of scope (from Linear + backlog):** - Runtime state, telegraphs, aggro, NPC instance registry ([NEO-91+](E5M2-prototype-backlog.md)). - Godot client wiring ([NEO-97](https://linear.app/neon-sprawl/issue/NEO-97)). - `docs/manual-qa/NEO-90.md` (kickoff decision). ## Acceptance criteria checklist - [x] GET returns all **three** prototype behavior defs with **`schemaVersion`** **1** and stable field names. - [x] Rows appear in ascending **`id`** order (ordinal). - [x] Bruno happy GET passes in CI Bruno step. ## Technical approach 1. **Route:** **`GET /game/world/npc-behavior-definitions`** — parameterless read; inject **`INpcBehaviorDefinitionRegistry`** only (not **`NpcBehaviorDefinitionCatalog`**). 2. **Response shape:** Top-level **`schemaVersion`** (`NpcBehaviorDefinitionsListResponse.CurrentSchemaVersion` = **1**) plus **`npcBehaviors`** array. Each row maps from **`NpcBehaviorDefRow`**: all nine schema fields with camelCase JSON names matching existing world-definition APIs. 3. **Ordering:** Build list from **`registry.GetDefinitionsInIdOrder()`** (already ordinal by id). Tests assert exact id sequence (frozen three from [E5.M2 freeze box](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01)): `prototype_elite_mini_boss`, `prototype_melee_pressure`, `prototype_ranged_control` (ordinal sort of the three ids — same order as **`NpcBehaviorDefinitionRegistryTests`** / **`PrototypeNpcBehaviorRegistry`** constants). 4. **Implementation:** New **`NpcBehaviorDefinitionsWorldApi`** + **`NpcBehaviorDefinitionsListDtos.cs`** in `Game/Npc/` (mirror [`AbilityDefinitionsWorldApi`](../../server/NeonSprawl.Server/Game/Combat/AbilityDefinitionsWorldApi.cs)). Wire **`app.MapNpcBehaviorDefinitionsWorldApi()`** from **`Program.cs`** after other world definition APIs (near **`MapAbilityDefinitionsWorldApi()`**). 5. **Bruno:** `bruno/neon-sprawl-server/npc-behavior-definitions/` with **`Get npc behavior definitions.bru`** + **`folder.bru`**. Tests: 200, JSON, **`schemaVersion` 1**, **`npcBehaviors.length === 3`**, ascending id order, **exact frozen-three registry sequence**, spot-check **`prototype_melee_pressure`** and **`prototype_elite_mini_boss`** metadata. 6. **Docs:** Add **`server/README.md`** subsection under NPC behavior catalog documenting GET + curl example. Update [E5_M2](E5_M2_NpcAiAndBehaviorProfiles.md) **Related implementation slices** and [documentation_and_implementation_alignment.md](documentation_and_implementation_alignment.md) E5.M2 row when landed. ### Expected prototype row values (from content) | `id` | `displayName` | `archetypeKind` | `maxHp` | `aggroRadius` | `leashRadius` | `telegraphWindupSeconds` | `attackDamage` | `attackCooldownSeconds` | |------|---------------|-----------------|---------|---------------|---------------|--------------------------|----------------|-------------------------| | `prototype_melee_pressure` | Melee Pressure | `melee_pressure` | 100 | 8.0 | 16.0 | 1.5 | 15 | 3.0 | | `prototype_ranged_control` | Ranged Control | `ranged_control` | 80 | 10.0 | 20.0 | 2.0 | 12 | 4.0 | | `prototype_elite_mini_boss` | Elite Mini-Boss | `elite_mini_boss` | 200 | 8.0 | 18.0 | 2.5 | 25 | 5.0 | ## Files to add | Path | Purpose | |------|---------| | `server/NeonSprawl.Server/Game/Npc/NpcBehaviorDefinitionsWorldApi.cs` | `Map*` extension registering GET route; maps registry → JSON. | | `server/NeonSprawl.Server/Game/Npc/NpcBehaviorDefinitionsListDtos.cs` | Versioned response + row DTOs for JSON serialization. | | `server/NeonSprawl.Server.Tests/Game/Npc/NpcBehaviorDefinitionsWorldApiTests.cs` | HTTP integration: 200, schema v1, three ids in order, spot-check rows. | | `bruno/neon-sprawl-server/npc-behavior-definitions/Get npc behavior definitions.bru` | Manual / Bruno verification against dev server. | | `bruno/neon-sprawl-server/npc-behavior-definitions/folder.bru` | Bruno folder metadata (match `ability-definitions`). | | `docs/plans/NEO-90-implementation-plan.md` | This plan. | ## Files to modify | Path | Rationale | |------|-----------| | `server/NeonSprawl.Server/Program.cs` | Register `MapNpcBehaviorDefinitionsWorldApi()` alongside other game world definition APIs. | | `server/README.md` | Document `GET /game/world/npc-behavior-definitions`, curl example, links to plan/Bruno. | | `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | **Related implementation slices** — HTTP read model bullet (NEO-90). | | `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E5.M2 row — note NEO-90 HTTP projection when landed. | | `docs/plans/E5M2-prototype-backlog.md` | E5M2-04 acceptance checkboxes + landed note when complete. | ## Tests | File | Coverage | |------|----------| | `server/NeonSprawl.Server.Tests/Game/Npc/NpcBehaviorDefinitionsWorldApiTests.cs` | **Integration:** `GET /game/world/npc-behavior-definitions` via **`InMemoryWebApplicationFactory`**; **`ReadFromJsonAsync`**; assert **`schemaVersion` 1**; **`npcBehaviors`** count **3**; ordered **`id`** list equals frozen three in ordinal id order; spot-check **`prototype_melee_pressure`** (maxHp 100, attackDamage 15, archetypeKind `melee_pressure`) and **`prototype_elite_mini_boss`** (maxHp 200, attackDamage 25, telegraphWindupSeconds 2.5). **AAA** per [csharp-style](../../.cursor/rules/csharp-style.md). | Bruno scripts complement automated tests for manual dev-server verification. No `docs/manual-qa/NEO-90.md` (server-only per kickoff). ## Open questions / risks | Question / risk | Agent recommendation | Status | |-----------------|---------------------|--------| | **Linear blockedBy NEO-89** | Branch from **`main`** where NEO-89 is merged (confirmed at kickoff). | **adopted** | | **Optional fields later** | When content adds new optional behavior-def fields, bump **`schemaVersion`** or extend v1 in a follow-up issue. | **deferred** | | **Client not in this story** | Cross-link **NEO-97** / **NEO-98**; Bruno is not prototype-complete per [full-stack epic decomposition](../../.cursor/rules/full-stack-epic-decomposition.md). | **adopted** | | **Numeric JSON types** | Emit radii/timings as JSON **number** (double); HP/damage as **integer**; tests assert numeric equality. | **adopted** | ## Decisions (kickoff) - **`npcBehaviors`** top-level array key — matches content catalog envelope (user confirmed). - **No manual QA doc** — server-only HTTP; Bruno + automated tests (user confirmed). - **Registry-only injection** — HTTP layer depends on **`INpcBehaviorDefinitionRegistry`**, not **`NpcBehaviorDefinitionCatalog`**. ## Reconciliation (implementation) - **`NpcBehaviorDefinitionsWorldApi`** + **`NpcBehaviorDefinitionsListDtos`** registered via **`MapNpcBehaviorDefinitionsWorldApi()`** in **`Program.cs`**; injects **`INpcBehaviorDefinitionRegistry`** only. - **`NpcBehaviorDefinitionsWorldApiTests`** — integration test for schema v1, frozen-three id order, **`prototype_melee_pressure`** / **`prototype_elite_mini_boss`** spot-checks. - **Bruno:** `bruno/neon-sprawl-server/npc-behavior-definitions/Get npc behavior definitions.bru` with response-shape tests. - **`server/README.md`**, **E5.M2** module doc, **alignment register**, and **E5M2 backlog** updated.