neon-sprawl/docs/plans/NEO-87-implementation-plan.md

129 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# NEO-87 — Implementation plan
## Story reference
| Field | Value |
|--------|--------|
| **Key** | NEO-87 |
| **Title** | E5M2-01: Prototype NpcBehaviorDef catalog + schemas + CI |
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-87/e5m2-01-prototype-npcbehaviordef-catalog-schemas-ci |
| **Module** | [E5.M2 — NpcAiAndBehaviorProfiles](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) · Epic 5 Slice 2 · backlog **E5M2-01** |
| **Branch** | `NEO-87-e5m2-npc-behavior-catalog-schemas-ci` |
| **Precursor** | [E5.M1](../decomposition/modules/E5_M1_CombatRulesEngine.md) **Ready** — passive dummies remain until E5M2-05 |
| **Pattern** | [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) — row JSON Schema + catalog envelope + `scripts/validate_content.py` gate |
| **Blocks** | [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) — server NPC behavior catalog load |
## Kickoff clarifications
| Topic | Question | Agent recommendation | Answer |
|--------|----------|----------------------|--------|
| **Radius / cooldown stat freeze** | `aggroRadius`, `leashRadius`, `attackCooldownSeconds` per archetype? | **Archetype-specific table** — melee 8/16/3.0 · ranged 10/20/4.0 · elite 8/18/5.0; radii sit above 6 m tab-lock; leash > aggro for anti-kite. | **User:** archetype-specific table (adopted below). |
| **Catalog envelope** | File shape? | `{ "schemaVersion": 1, "npcBehaviors": [ … ] }` in `content/npc-behaviors/prototype_npc_behaviors.json` (mirror abilities / items). | **Adopted** (NEO-76 precedent). |
| **CI constant name** | Slice gate identifier? | **`PROTOTYPE_E5M2_NPC_BEHAVIOR_IDS`** — Epic 5 Slice 2; avoids collision with E5M1 ability gate. | **Adopted**. |
| **Client counterpart** | Godot issue for this story? | **None** — server-only content + CI; player-visible work starts at E5M2-04+ / [NEO-97](https://linear.app/neon-sprawl/issue/NEO-97) / [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98). | **Adopted** (E5M2-01 out of scope). |
## Goal, scope, and out-of-scope
**Goal:** Lock content shape and CI validation for **three** frozen `NpcBehaviorDef` rows before server load (NEO-88+).
**In scope (from Linear + [E5M2-01](E5M2-prototype-backlog.md#e5m2-01--prototype-npcbehaviordef-catalog--schemas--ci)):**
- `content/schemas/npc-behavior-def.schema.json`.
- `content/npc-behaviors/prototype_npc_behaviors.json` — three frozen ids (full freeze table below).
- `scripts/validate_content.py` — schema validation, duplicate `id`, exact three-id allowlist, positive numeric guards (`leashRadius` > `aggroRadius`).
- Expand **Prototype Slice 2 freeze** table in [E5_M2](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) + designer note in `content/README.md`.
- **CT.M1** PR gate paragraph for NPC behavior catalogs.
- `documentation_and_implementation_alignment.md` E5.M2 row → note NEO-87 catalog in progress / landed.
**Out of scope (from Linear):**
- Server loader, runtime engine, HTTP, Godot (NEO-88+).
- NPC instance registry / dummy migration (E5M2-05).
## Acceptance criteria checklist
- [x] PR gate validates NPC behavior JSON against schema.
- [x] Exactly three prototype behavior ids; duplicate `id` fails CI.
- [x] Stable id list documented in module doc freeze box.
## Implementation reconciliation (shipped)
- **Schema:** [`npc-behavior-def.schema.json`](../../content/schemas/npc-behavior-def.schema.json) — required `id`, `displayName`, `archetypeKind`, `maxHp`, `aggroRadius`, `leashRadius`, `telegraphWindupSeconds`, `attackDamage`, `attackCooldownSeconds`.
- **Catalog:** [`prototype_npc_behaviors.json`](../../content/npc-behaviors/prototype_npc_behaviors.json) — three frozen rows per kickoff stat table.
- **CI:** `PROTOTYPE_E5M2_NPC_BEHAVIOR_IDS` gate + `leashRadius > aggroRadius` guard in [`validate_content.py`](../../scripts/validate_content.py).
- **Docs:** E5.M2 full freeze table, `content/README.md`, CT.M1, alignment register, E5M2-01 backlog checkboxes.
## Technical approach
1. **Row schema (`npc-behavior-def.schema.json`):** Draft 2020-12 object; `additionalProperties: false`. Required: `id`, `displayName`, `archetypeKind`, `maxHp`, `aggroRadius`, `leashRadius`, `telegraphWindupSeconds`, `attackDamage`, `attackCooldownSeconds`. `id` pattern `^[a-z][a-z0-9_]*$`. `displayName` `minLength: 1`. `archetypeKind` enum `melee_pressure` \| `ranged_control` \| `elite_mini_boss`. `maxHp` integer ≥ 1. `attackDamage` integer ≥ 1. `aggroRadius`, `leashRadius`, `telegraphWindupSeconds`, `attackCooldownSeconds` number with `exclusiveMinimum: 0` (strictly positive).
2. **Catalog file:** `content/npc-behaviors/prototype_npc_behaviors.json` with `schemaVersion: 1` and three rows from the kickoff-adopted freeze table.
3. **Frozen content values (E5M2-01 — kickoff adopted):**
| `NpcBehaviorDef.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 |
**Rules:** Do **not** rename frozen behavior **`id`** values after ship — change **`displayName`** only, or add a new `id` in a follow-up issue. CI enforces **exactly** these three ids. Tune combat feel via numeric fields or new ids in a follow-up issue.
4. **`validate_content.py`:**
- Add `NPC_BEHAVIOR_SCHEMA`, `NPC_BEHAVIORS_DIR`; discover `content/npc-behaviors/*_npc_behaviors.json`.
- `_validate_npc_behavior_catalogs`: envelope `schemaVersion: 1`, top-level `npcBehaviors` array; validate each row against `npc-behavior-def.schema.json`; track `seen_ids`; reject duplicates across files.
- **`_prototype_e5m2_npc_behavior_gate`:** ids must equal `PROTOTYPE_E5M2_NPC_BEHAVIOR_IDS` (frozenset of three ids above).
- **`_prototype_e5m2_npc_behavior_numeric_gate`:** after schema pass, require `leashRadius > aggroRadius` per row (backlog anti-kite invariant).
- Run NPC behavior validation after ability catalogs succeed (no cross-catalog refs in E5M2-01).
- Require NPC behavior schema file in `main()` startup checks; extend module docstring + success summary with npc-behavior file + id counts.
- Keep `PROTOTYPE_E5M2_NPC_BEHAVIOR_IDS` in sync with [E5.M2 freeze table](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01) and future NEO-88 server loader.
5. **Docs:**
- `content/README.md` — add `npc-behaviors/` table row + E5 Slice 2 NPC behavior freeze paragraph.
- `E5_M2_NpcAiAndBehaviorProfiles.md` — expand **Prototype Slice 2 freeze (E5M2-01)** table with full column set + CI rules line.
- `CT_M1_ContentValidationPipeline.md` — NPC behavior catalog PR gate paragraph.
- `documentation_and_implementation_alignment.md` — E5.M2 row notes NEO-87 catalog when complete.
- `E5M2-prototype-backlog.md` — E5M2-01 checkboxes when implementation completes.
6. **No server/C#/client changes** in this story.
## Files to add
| Path | Purpose |
|------|---------|
| `content/schemas/npc-behavior-def.schema.json` | JSON Schema for a single `NpcBehaviorDef` row. |
| `content/npc-behaviors/prototype_npc_behaviors.json` | Prototype three-behavior catalog (`schemaVersion` + `npcBehaviors` array). |
| `docs/plans/NEO-87-implementation-plan.md` | This plan. |
## Files to modify
| Path | Rationale |
|------|-----------|
| `scripts/validate_content.py` | Load/validate NPC behavior catalogs; duplicate `id`; E5M2 three-id gate; leash > aggro guard; success summary. |
| `content/README.md` | Document `content/npc-behaviors/`, schema path, E5 Slice 2 freeze (active catalog). |
| `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | Full freeze table + CI enforcement note; link to plan when complete. |
| `docs/decomposition/modules/CT_M1_ContentValidationPipeline.md` | PR gate paragraph for NPC behavior catalog validation. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E5.M2 / NEO-87 status after catalog lands. |
| `docs/plans/E5M2-prototype-backlog.md` | E5M2-01 checkboxes + landed note when complete. |
## Tests
| Item | Coverage |
|------|----------|
| **CI / PR gate** | `.github/workflows/pr-gate.yml` already runs `python scripts/validate_content.py` — extended script is the regression signal. |
| **Manual negative cases** | From repo root after implementation: (1) duplicate behavior `id` → non-zero exit; (2) remove one frozen id → gate error; (3) break schema (zero `aggroRadius` or `leashRadius <= aggroRadius`) → schema or numeric gate error; (4) empty `displayName` → schema error. |
| **Manual happy path** | `pip install -r scripts/requirements-content.txt && python3 scripts/validate_content.py` — reports npc-behavior catalog + existing catalogs OK. |
No dedicated pytest module in-repo today (same as NEO-76 / NEO-50).
## Open questions / risks
| Question / risk | Agent recommendation | Status |
|-----------------|----------------------|--------|
| **Catalog growth** | E5M2 gate requires **exactly** the three frozen ids; expanding the roster needs a follow-up issue to change `PROTOTYPE_E5M2_NPC_BEHAVIOR_IDS`. | **adopted** |
| **C# vs Python drift** | None until NEO-88; document “keep in sync” constant names in both places when server load lands (mirror NEO-77 / `PROTOTYPE_E5M1_ABILITY_IDS`). | **adopted** |
| **Radii vs tab-lock 6 m** | `aggroRadius` 810 m keeps proximity re-aggro usable in the combat pocket without matching tab-lock exactly; E5M2-06 distance checks use behavior-def radii, not `PrototypeTargetRegistry.SharedLockRadius`. | **adopted** |
| **Runtime still uses alpha/beta dummies** | Expected — content lands before E5M2-05 migrates combat targets to NPC instance ids. | **adopted** |
None blocking.