From 846da902197d42282ed2358398f947752d28bd0d Mon Sep 17 00:00:00 2001 From: VinPropane Date: Mon, 25 May 2026 16:51:55 -0400 Subject: [PATCH] NEO-87: add E5M2-01 NPC behavior catalog kickoff plan. Lock three-archetype NpcBehaviorDef content shape, CI gates, and adopted radius/cooldown stat freeze before NEO-88 server load. --- docs/plans/NEO-87-implementation-plan.md | 121 +++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 docs/plans/NEO-87-implementation-plan.md diff --git a/docs/plans/NEO-87-implementation-plan.md b/docs/plans/NEO-87-implementation-plan.md new file mode 100644 index 0000000..66343c0 --- /dev/null +++ b/docs/plans/NEO-87-implementation-plan.md @@ -0,0 +1,121 @@ +# 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 + +- [ ] PR gate validates NPC behavior JSON against schema. +- [ ] Exactly three prototype behavior ids; duplicate `id` fails CI. +- [ ] Stable id list documented in module doc freeze box. + +## 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` 8–10 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.