# 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.
