diff --git a/docs/plans/NEO-90-implementation-plan.md b/docs/plans/NEO-90-implementation-plan.md new file mode 100644 index 0000000..bafdc82 --- /dev/null +++ b/docs/plans/NEO-90-implementation-plan.md @@ -0,0 +1,118 @@ +# 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 + +- [ ] GET returns all **three** prototype behavior defs with **`schemaVersion`** **1** and stable field names. +- [ ] Rows appear in ascending **`id`** order (ordinal). +- [ ] 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`**.