From db9ddb9fe03198e60feeba52c99f67b1fdfa5af6 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sat, 30 May 2026 18:38:18 -0400 Subject: [PATCH 1/2] =?UTF-8?q?chore:=20Decompose=20E5.M3=20prototype=20ba?= =?UTF-8?q?cklog=20(NEO-100=E2=80=93NEO-111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Epic 5 Slice 3 encounter + reward routing: backlog doc, module/epic/register updates, and Linear issues for one combat-pocket clear with idempotent loot. --- content/README.md | 4 + .../decomposition/epics/epic_05_pve_combat.md | 4 +- .../modules/E5_M3_EncounterAndRewardTables.md | 39 +- ...umentation_and_implementation_alignment.md | 1 + .../modules/module_dependency_register.md | 4 +- docs/plans/E5M2-prototype-backlog.md | 2 +- docs/plans/E5M3-prototype-backlog.md | 375 ++++++++++++++++++ 7 files changed, 424 insertions(+), 5 deletions(-) create mode 100644 docs/plans/E5M3-prototype-backlog.md diff --git a/content/README.md b/content/README.md index 843f4d8..0017067 100644 --- a/content/README.md +++ b/content/README.md @@ -11,6 +11,8 @@ Data-driven definitions (skills, items, recipes, quests) validated in CI with ** | [`recipes/`](recipes/) | Recipe catalogs; each row matches [`schemas/recipe-def.schema.json`](schemas/recipe-def.schema.json) — **stable `id`**, **`recipeKind`**, **`requiredSkillId`**, inputs/outputs for [E3.M2](../docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md) | | [`abilities/`](abilities/) | Combat ability catalogs; each row matches [`schemas/ability-def.schema.json`](schemas/ability-def.schema.json) — **stable `id`**, **`baseDamage`**, **`cooldownSeconds`** for [E5.M1](../docs/decomposition/modules/E5_M1_CombatRulesEngine.md) | | [`npc-behaviors/`](npc-behaviors/) | NPC behavior archetype catalogs; each row matches [`schemas/npc-behavior-def.schema.json`](schemas/npc-behavior-def.schema.json) — **stable `id`**, **`archetypeKind`**, aggro/leash radii, telegraph + attack timings for [E5.M2](../docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) | +| [`encounters/`](encounters/) | Encounter templates; each row matches [`schemas/encounter-def.schema.json`](schemas/encounter-def.schema.json) — **stable `id`**, completion criteria, **`rewardTableId`** for [E5.M3](../docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md) | +| [`reward-tables/`](reward-tables/) | Reward table catalogs; each row matches [`schemas/reward-table.schema.json`](schemas/reward-table.schema.json) — **stable `id`**, **`fixedGrants`** referencing frozen item ids for [E5.M3](../docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md) | | [`resource-nodes/`](resource-nodes/) | Resource node + yield catalogs; node rows match [`schemas/resource-node-def.schema.json`](schemas/resource-node-def.schema.json), yield rows match [`schemas/resource-yield-row.schema.json`](schemas/resource-yield-row.schema.json) — **stable `nodeDefId`**, **`gatherLens`**, **`maxGathers`** for [E3.M1](../docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) | **Prototype Slice 1 (NEO-33):** CI expects **exactly three** skill rows with ids **`salvage`**, **`refine`**, **`intrusion`** (gather + process + tech). Each row must declare **`allowedXpSourceKinds`** (non-empty); see [E2.M1 — Designer note: `allowedXpSourceKinds`](../docs/decomposition/modules/E2_M1_SkillDefinitionRegistry.md#designer-note-allowedxpsourcekinds-and-grant-call-sites) for what each kind means for grant wiring. @@ -25,6 +27,8 @@ Data-driven definitions (skills, items, recipes, quests) validated in CI with ** **Prototype E5 Slice 2 — NPC behaviors (NEO-87):** CI expects **exactly three** npc behavior ids aligned to [E5.M2 behavior freeze](../docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01) — **`prototype_melee_pressure`**, **`prototype_ranged_control`**, **`prototype_elite_mini_boss`**. Each row requires **`leashRadius` > `aggroRadius`**. **Do not rename** behavior `id` after ship—change **`displayName`** only. See [E5M2-prototype-backlog.md](../docs/plans/E5M2-prototype-backlog.md) and [NEO-87 plan](../docs/plans/NEO-87-implementation-plan.md). +**Prototype E5 Slice 3 — encounters + rewards (NEO-100):** CI expects **exactly one** encounter id **`prototype_combat_pocket`** and one reward table id **`prototype_combat_pocket_clear`** aligned to [E5.M3 encounter freeze](../docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md#prototype-slice-3-freeze-e5m3-01). **`requiredNpcInstanceIds`** must match the three E5.M2 NPC instance ids; **`fixedGrants`** reference frozen item ids only. **Do not rename** ids after ship—change **`displayName`** only. See [E5M3-prototype-backlog.md](../docs/plans/E5M3-prototype-backlog.md). + **Prototype Slice 4 (NEO-45):** CI expects **exactly one** `MasteryTrack` for **`salvage`** only (`refine` / `intrusion` have no mastery rows yet). Tier 1 @ skill level **2** is a **mutually exclusive** branch pick (`scrap_efficiency` vs `bulk_haul`) with **no** perks; tier 2 @ level **4** unlocks one perk per branch path. **Do not rename** `branchId` / `perkId` after ship—change `displayName` only. See [E2.M3 — Designer note](../docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md#designer-note-tiers-branches-and-level-gates) and [freeze table](../docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md#prototype-slice-4-freeze--salvage-flagship-neo-45). Server load path and hot-reload policy are TBD; keep files versioned here as the source of truth. diff --git a/docs/decomposition/epics/epic_05_pve_combat.md b/docs/decomposition/epics/epic_05_pve_combat.md index 5249680..910a200 100644 --- a/docs/decomposition/epics/epic_05_pve_combat.md +++ b/docs/decomposition/epics/epic_05_pve_combat.md @@ -69,11 +69,13 @@ Deliver tab-target combat resolution, NPC behaviors and telegraphs, encounter te ### Slice 3 - Encounters and loot routing - Scope: E5.M3 wired to E3.M3 and E7.M2 for rewards and quest progress. -- Dependencies: E5.M2, E3.M3, E7.M2 +- Dependencies: E5.M2, E3.M3, E7.M2 (quest router stub + quest-token loot until E7.M2 lands) - Acceptance criteria: - Completing defined encounters grants expected items/quest credit once. - Telemetry hooks: `encounter_complete`, reward attribution. +**Linear backlog:** [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md) — [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) → [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111); client capstone **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111). Verify in **Godot**, not Bruno-only. + ### Slice 4 - Group scaling (pre-production) - Scope: E5.M4 with party-sized modifiers behind E8.M1. diff --git a/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md b/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md index 5a84ba9..41fc246 100644 --- a/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md +++ b/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md @@ -7,7 +7,8 @@ | **Module ID** | E5.M3 | | **Epic** | [Epic 5 — PvE Combat](../epics/epic_05_pve_combat.md) | | **Stage target** | Prototype | -| **Status** | Planned (see [dependency register](module_dependency_register.md)) | +| **Status** | In Progress — Slice 3 backlog [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md): **E5M3-01** [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) → **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) decomposed | +| **Linear** | Label **`E5.M3`** · [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md) **E5M3-01** [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) · **E5M3-02** [NEO-101](https://linear.app/neon-sprawl/issue/NEO-101) · **E5M3-03** [NEO-102](https://linear.app/neon-sprawl/issue/NEO-102) · **E5M3-04** [NEO-103](https://linear.app/neon-sprawl/issue/NEO-103) · **E5M3-05** [NEO-104](https://linear.app/neon-sprawl/issue/NEO-104) · **E5M3-06** [NEO-105](https://linear.app/neon-sprawl/issue/NEO-105) · **E5M3-07** [NEO-106](https://linear.app/neon-sprawl/issue/NEO-106) · **E5M3-08** [NEO-107](https://linear.app/neon-sprawl/issue/NEO-107) · **E5M3-09** [NEO-108](https://linear.app/neon-sprawl/issue/NEO-108) · **E5M3-10** [NEO-109](https://linear.app/neon-sprawl/issue/NEO-109) · **E5M3-11** [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) · **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) | ## Purpose @@ -28,8 +29,9 @@ Encounter templates, completion criteria, and reward drop routing into items ([E ## Module dependencies - **E5.M1** — CombatRulesEngine. +- **E5.M2** — NpcAiAndBehaviorProfiles (defeat targets). - **E3.M3** — ItemizationAndInventorySchema. -- **E7.M2** — RewardAndUnlockRouter. +- **E7.M2** — RewardAndUnlockRouter (quest credit consumer; prototype uses quest-token loot + hook stub). ## Dependents (by design) @@ -41,6 +43,39 @@ Encounter templates, completion criteria, and reward drop routing into items ([E Epic 5 **Slice 3** — `encounter_complete`, reward attribution once. +## Linear backlog (decomposed) + +Full story tables, kickoff defaults, and **`blockedBy` graph:** [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md). + +| Slug | Linear | +|------|--------| +| E5M3-01 | [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) | +| E5M3-02 | [NEO-101](https://linear.app/neon-sprawl/issue/NEO-101) | +| E5M3-03 | [NEO-102](https://linear.app/neon-sprawl/issue/NEO-102) | +| E5M3-04 | [NEO-103](https://linear.app/neon-sprawl/issue/NEO-103) | +| E5M3-05 | [NEO-104](https://linear.app/neon-sprawl/issue/NEO-104) | +| E5M3-06 | [NEO-105](https://linear.app/neon-sprawl/issue/NEO-105) | +| E5M3-07 | [NEO-106](https://linear.app/neon-sprawl/issue/NEO-106) | +| E5M3-08 | [NEO-108](https://linear.app/neon-sprawl/issue/NEO-108) | +| E5M3-09 | [NEO-107](https://linear.app/neon-sprawl/issue/NEO-107) | +| E5M3-10 | [NEO-109](https://linear.app/neon-sprawl/issue/NEO-109) | +| E5M3-11 | [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) | +| E5M3-12 | [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) | + +## Prototype Slice 3 freeze (E5M3-01) + +The **first shipped encounter + reward spine** is **frozen** for prototype tuning until a deliberate migration issue expands the roster. + +| `EncounterDef.id` | `displayName` | `completionCriteria.kind` | `requiredNpcInstanceIds` | `rewardTableId` | +|-------------------|---------------|---------------------------|--------------------------|-----------------| +| **`prototype_combat_pocket`** | Prototype Combat Pocket | **`defeat_all_targets`** | **`prototype_npc_melee`**, **`prototype_npc_ranged`**, **`prototype_npc_elite`** | **`prototype_combat_pocket_clear`** | + +| `RewardTable.id` | `fixedGrants` | +|------------------|---------------| +| **`prototype_combat_pocket_clear`** | **`scrap_metal_bulk` ×10**, **`contract_handoff_token` ×1** | + +**Payout policy:** Per-defeat **gig XP** stays on [NEO-44](../../plans/NEO-44-implementation-plan.md). Encounter complete grants **loot + quest token** **once** per player per encounter id. Full **E7.M2** quest credit routing consumes **`EncounterCompleteEvent`** (E5M3-09 hook stub). + ## Source anchors - Master plan: [`neon_sprawl_vision.plan.md`](../../../neon_sprawl_vision.plan.md) — Epic 5. diff --git a/docs/decomposition/modules/documentation_and_implementation_alignment.md b/docs/decomposition/modules/documentation_and_implementation_alignment.md index 576eb48..2ec6ffa 100644 --- a/docs/decomposition/modules/documentation_and_implementation_alignment.md +++ b/docs/decomposition/modules/documentation_and_implementation_alignment.md @@ -58,6 +58,7 @@ Rows appear when work starts; default for unlisted modules is **Planned** / not | E3.M2 | Ready | **NEO-65 landed:** frozen prototype eight-recipe catalog in [`content/recipes/prototype_recipes.json`](../../../content/recipes/prototype_recipes.json); [`recipe-def.schema.json`](../../../content/schemas/recipe-def.schema.json) + [`recipe-io-row.schema.json`](../../../content/schemas/recipe-io-row.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) Slice 3 gates ([NEO-65](../../plans/NEO-65-implementation-plan.md)). **NEO-66 landed:** fail-fast server load of `content/recipes/*_recipes.json` at startup — `server/NeonSprawl.Server/Game/Crafting/` ([NEO-66](../../plans/NEO-66-implementation-plan.md)); [server README — Recipe catalog](../../../server/README.md#recipe-catalog-contentrecipes-neo-66). **NEO-67 landed:** injectable **`IRecipeDefinitionRegistry`** + lookup tests ([NEO-67](../../plans/NEO-67-implementation-plan.md)). **NEO-68 landed:** **`GET /game/world/recipe-definitions`** — `RecipeDefinitionsWorldApi` + DTOs in `Game/Crafting/` ([NEO-68](../../plans/NEO-68-implementation-plan.md), [`NEO-68` manual QA](../../manual-qa/NEO-68.md)); [server README — Recipe definitions (NEO-68)](../../../server/README.md#recipe-definitions-neo-68); Bruno `bruno/neon-sprawl-server/recipe-definitions/`. **NEO-69 landed:** **`CraftOperations.TryCraft`** + **`CraftResult`** — pre-flight inputs/outputs, inventory mutation, refine XP ([NEO-69](../../plans/NEO-69-implementation-plan.md)); [server README — Craft engine (NEO-69)](../../../server/README.md#craft-engine-neo-69-and-craft-http-neo-70). **NEO-70 landed:** **`POST /game/players/{id}/craft`** — `PlayerCraftApi` + wire **`CraftResponse`**; Bruno gather→refine→make spine ([NEO-70](../../plans/NEO-70-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/craft/`. **NEO-71 landed:** comment-only **`item_crafted`** / **`craft_failed`** telemetry hook sites in **`CraftOperations.TryCraft`** ([NEO-71](../../plans/NEO-71-implementation-plan.md)); [server README — Craft telemetry hooks (NEO-71)](../../../server/README.md#craft-telemetry-hooks-neo-71). **NEO-42 landed (prep):** **`RefineActivitySkillXpGrant`** / **`RefineSkillXpConstants`** — wired from craft engine success path. Epic 3 Slice 3 server backlog **E3M2-01**–**E3M2-07** complete. Upstream: E3.M1 **Ready**, E3.M3 inventory landed. **NEO-74 landed:** client craft UI — **`recipe_definitions_client.gd`**, **`craft_client.gd`**, **`craft_recipe_panel.gd`**, **`CraftFeedbackLabel`**, **refine** skill row ([NEO-74](../../plans/NEO-74-implementation-plan.md), [`NEO-74` manual QA](../../manual-qa/NEO-74.md)); `client/README.md` craft section. **NEO-75 landed:** capstone playable gather→refine→make loop — [`NEO-75` manual QA](../../manual-qa/NEO-75.md), **`prototype_economy_hud_section.gd`**; Epic 3 Slice 5 client complete. | [NEO-42](../../plans/NEO-42-implementation-plan.md), [NEO-65](../../plans/NEO-65-implementation-plan.md), [NEO-66](../../plans/NEO-66-implementation-plan.md), [NEO-67](../../plans/NEO-67-implementation-plan.md), [NEO-68](../../plans/NEO-68-implementation-plan.md), [NEO-69](../../plans/NEO-69-implementation-plan.md), [NEO-70](../../plans/NEO-70-implementation-plan.md), [NEO-71](../../plans/NEO-71-implementation-plan.md), [NEO-74](../../plans/NEO-74-implementation-plan.md), [NEO-75](../../plans/NEO-75-implementation-plan.md), [E3M2-prototype-backlog](../../plans/E3M2-prototype-backlog.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3_M2](E3_M2_RefinementAndRecipeExecution.md) | | E5.M1 | Ready | **NEO-76 landed:** frozen prototype four-ability catalog in [`content/abilities/prototype_abilities.json`](../../../content/abilities/prototype_abilities.json); [`ability-def.schema.json`](../../../content/schemas/ability-def.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) E5M1 four-id gate ([NEO-76](../../plans/NEO-76-implementation-plan.md)). **NEO-77 landed:** fail-fast server load of `content/abilities/*_abilities.json` at startup — `server/NeonSprawl.Server/Game/Combat/` ([NEO-77](../../plans/NEO-77-implementation-plan.md)); [server README — Ability catalog](../../../server/README.md#ability-catalog-contentabilities-neo-77). **NEO-79 landed:** injectable **`IAbilityDefinitionRegistry`** + DI; hotbar/cast use **`TryNormalizeKnown`** ([NEO-79](../../plans/NEO-79-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/hotbar-loadout/` + `ability-cast/` unknown-ability deny smokes. **NEO-78 landed:** **`GET /game/world/ability-definitions`** — `AbilityDefinitionsWorldApi` + DTOs in `Game/Combat/` ([NEO-78](../../plans/NEO-78-implementation-plan.md)); [server README — Ability definitions (NEO-78)](../../../server/README.md#ability-definitions-neo-78); Bruno `bruno/neon-sprawl-server/ability-definitions/`. **NEO-80 landed:** **`ICombatEntityHealthStore`** / **`InMemoryCombatEntityHealthStore`** — lazy init (Slice 1 flat 100 HP; **superseded by [NEO-91](../../plans/NEO-91-implementation-plan.md)** per-archetype catalog max HP + three NPC instance ids); DI via **`AddCombatEntityHealthStore()`** ([NEO-80](../../plans/NEO-80-implementation-plan.md)); [server README — Combat entity health (NEO-80, NEO-91)](../../../server/README.md#combat-entity-health-neo-80-neo-91). **NEO-81 landed:** **`CombatOperations.TryResolve`** + **`CombatResult`** + **`CombatReasonCodes`** — defeated pre-check deny, catalog damage ([NEO-81](../../plans/NEO-81-implementation-plan.md)); [server README — Combat engine (NEO-81)](../../../server/README.md#combat-engine-neo-81). **NEO-82 landed:** **`AbilityCastApi`** → **`CombatOperations.TryResolve`**; nested wire **`combatResolution`** on accept; per-ability catalog cooldown; **`target_defeated`** cast deny ([NEO-82](../../plans/NEO-82-implementation-plan.md)); [server README — Ability cast (NEO-82)](../../../server/README.md#ability-cast-neo-31-neo-82); Bruno `bruno/neon-sprawl-server/ability-cast/` defeat spine. **NEO-83 landed:** **`GET /game/world/combat-targets`** — `CombatTargetsWorldApi` + DTOs; authoritative HP snapshot from **`ICombatEntityHealthStore`** ([NEO-83](../../plans/NEO-83-implementation-plan.md)); [server README — Combat targets snapshot (NEO-83)](../../../server/README.md#combat-targets-snapshot-neo-83); Bruno `bruno/neon-sprawl-server/combat-targets/`. **NEO-44 landed:** **`CombatDefeatGigXpGrant`** on cast **`targetDefeated`** — **25** gig XP to **`breach`** via **`IPlayerGigProgressionStore`** (V007); **`GET …/gig-progression`** ([NEO-44](../../plans/NEO-44-implementation-plan.md), [`NEO-44` manual QA](../../manual-qa/NEO-44.md)); [server README — Gig progression (NEO-44)](../../../server/README.md#gig-progression-snapshot-neo-44); Bruno `bruno/neon-sprawl-server/gig-progression/`. **NEO-84 landed:** comment-only **`ability_used`** / **`enemy_defeat`** telemetry hook sites in **`CombatOperations.TryResolve`**; reserved **`encounter_start`** / **`player_death`** ([NEO-84](../../plans/NEO-84-implementation-plan.md)); [server README — Combat telemetry hooks (NEO-84)](../../../server/README.md#combat-telemetry-hooks-neo-84). **NEO-85 landed:** client combat HUD — **`ability_cast_client.gd`** parses **`combatResolution`**; **`combat_targets_client.gd`**, **`CastFeedbackLabel`** resolution copy, **`CombatTargetHpLabel`**; event-driven GET refresh ([NEO-85](../../plans/NEO-85-implementation-plan.md), [`NEO-85` manual QA](../../manual-qa/NEO-85.md)); `client/README.md` combat HUD section. **NEO-86 landed:** playable combat capstone — **`gig_progression_client.gd`**, **`GigXpLabel`**, defeat-triggered gig GET refresh; capstone manual QA [`NEO-86`](../../manual-qa/NEO-86.md); `client/README.md` end-to-end combat loop section ([NEO-86](../../plans/NEO-86-implementation-plan.md)). **Epic 5 Slice 1 client capstone complete.** **Backlog decomposed:** Epic 5 Slice 1 — **E5M1-01**–**E5M1-12** landed. **Precursor landed:** E1.M4 cast funnel (NEO-28/31/32). | [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md), [E5_M1](E5_M1_CombatRulesEngine.md), [NEO-77](../../plans/NEO-77-implementation-plan.md), [NEO-79](../../plans/NEO-79-implementation-plan.md), [NEO-78](../../plans/NEO-78-implementation-plan.md), [NEO-80](../../plans/NEO-80-implementation-plan.md), [NEO-81](../../plans/NEO-81-implementation-plan.md), [NEO-82](../../plans/NEO-82-implementation-plan.md), [NEO-83](../../plans/NEO-83-implementation-plan.md), [NEO-44](../../plans/NEO-44-implementation-plan.md), [NEO-84](../../plans/NEO-84-implementation-plan.md), [NEO-85](../../plans/NEO-85-implementation-plan.md), [NEO-86](../../plans/NEO-86-implementation-plan.md), [E1M4-prototype-backlog.md](../../plans/E1M4-prototype-backlog.md) | | E5.M2 | Ready | **NEO-87 landed:** frozen prototype three-behavior catalog in [`content/npc-behaviors/prototype_npc_behaviors.json`](../../../content/npc-behaviors/prototype_npc_behaviors.json); [`npc-behavior-def.schema.json`](../../../content/schemas/npc-behavior-def.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) E5M2 three-id gate ([NEO-87](../../plans/NEO-87-implementation-plan.md)). **NEO-88 landed:** fail-fast server load of `content/npc-behaviors/*_npc_behaviors.json` at startup — `server/NeonSprawl.Server/Game/Npc/` ([NEO-88](../../plans/NEO-88-implementation-plan.md)); [server README — NPC behavior catalog](../../../server/README.md#npc-behavior-catalog-contentnpc-behaviors-neo-88). **NEO-89 landed:** injectable **`INpcBehaviorDefinitionRegistry`** + DI; **`PrototypeNpcBehaviorRegistry`** id constants ([NEO-89](../../plans/NEO-89-implementation-plan.md)). **NEO-90 landed:** **`GET /game/world/npc-behavior-definitions`** — `NpcBehaviorDefinitionsWorldApi` + DTOs in `Game/Npc/` ([NEO-90](../../plans/NEO-90-implementation-plan.md)); [server README — NPC behavior definitions (NEO-90)](../../../server/README.md#npc-behavior-definitions-neo-90); Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`. **NEO-91 landed:** **`PrototypeNpcRegistry`** + combat-target migration — three NPC instance ids replace alpha/beta; per-archetype max HP from behavior catalog ([NEO-91](../../plans/NEO-91-implementation-plan.md)); [server README — Combat entity health (NEO-91)](../../../server/README.md#combat-entity-health-neo-80-neo-91). **NEO-92 landed:** **`IThreatStateStore`** + **`AggroOperations`** — first-hit acquire on damaging cast, leash clear on move/cast; cast/move/fixture hooks ([NEO-92](../../plans/NEO-92-implementation-plan.md)); [server README — Threat / aggro state (NEO-92)](../../../server/README.md#threat--aggro-state-neo-92). **NEO-93 landed:** **`INpcRuntimeStateStore`** + **`NpcRuntimeOperations.AdvanceAll`** — catalog-driven behavior state machine, lazy tick advance with delta cap; dev fixture reset ([NEO-93](../../plans/NEO-93-implementation-plan.md)); [server README — NPC runtime behavior state (NEO-93)](../../../server/README.md#npc-runtime-behavior-state-neo-93). **NEO-94 landed:** **`GET /game/world/npc-runtime-snapshot`** — `NpcRuntimeSnapshotWorldApi` + DTOs; lazy **`AdvanceAll`** on poll; nested **`activeTelegraph`** with server-computed **`windupRemainingSeconds`** ([NEO-94](../../plans/NEO-94-implementation-plan.md)); [server README — NPC runtime snapshot (NEO-94)](../../../server/README.md#npc-runtime-snapshot-neo-94); Bruno `bruno/neon-sprawl-server/npc-runtime-snapshot/`. **NEO-95 landed:** **`IPlayerCombatHealthStore`** + **`NpcAttackOperations.TryResolveTelegraphComplete`** — telegraph resolve via behavior **`attackAbilityId`** + ability **`maxRange`** / **`baseDamage`** ([NEO-95](../../plans/NEO-95-implementation-plan.md), [NEO-98](../../plans/NEO-98-implementation-plan.md)); [server README — Session player combat HP (NEO-95)](../../../server/README.md#session-player-combat-hp-neo-95); Bruno `bruno/neon-sprawl-server/combat-health/`. **NEO-96 landed:** comment-only **`telegraph_fired`** + **`npc_state_transition`** hook sites in **`NpcRuntimeOperations.AdvanceAll`** ([NEO-96](../../plans/NEO-96-implementation-plan.md)); [server README — NPC runtime telemetry hooks (NEO-96)](../../../server/README.md#npc-runtime-telemetry-hooks-neo-96). **NEO-97 landed:** client telegraph HUD — **`npc_runtime_client.gd`**, **`player_combat_health_client.gd`**, **`npc_runtime_hud_state.gd`**, **`TelegraphLabel`** / **`NpcStateLabel`** / **`PlayerCombatHpLabel`**; combat-active ~1 Hz poll ([NEO-97](../../plans/NEO-97-implementation-plan.md), [`NEO-97` manual QA](../../manual-qa/NEO-97.md)); [client README — NPC runtime + telegraph HUD (NEO-97)](../../../client/README.md#npc-runtime--telegraph-hud-neo-97). **NEO-98 landed:** playable NPC telegraph combat capstone — [`NEO-98` manual QA](../../manual-qa/NEO-98.md); `client/README.md` end-to-end NPC telegraph combat loop section ([NEO-98](../../plans/NEO-98-implementation-plan.md)). **NEO-98 server integration:** defeat clears aggro/runtime (**`TryStopOnTargetDefeat`**); telegraph damage range-gated via [`prototype_npc_abilities.json`](../../../content/abilities/prototype_npc_abilities.json) + behavior **`attackAbilityId`**. **Epic 5 Slice 2 client capstone complete.** **Backlog decomposed:** Epic 5 Slice 2 — [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md) **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) **landed**. Three archetypes (`prototype_melee_pressure`, `prototype_ranged_control`, `prototype_elite_mini_boss`); replaces alpha/beta dummies with **`prototype_npc_melee` / `ranged` / `elite`**. Owns **`ThreatState`**, **`TelegraphEvent`**, **`GET …/npc-runtime-snapshot`**, session **`IPlayerCombatHealthStore`**. Upstream **E5.M1 Ready**. | [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md), [E5_M2](E5_M2_NpcAiAndBehaviorProfiles.md), [NEO-87](../../plans/NEO-87-implementation-plan.md), [NEO-88](../../plans/NEO-88-implementation-plan.md), [NEO-89](../../plans/NEO-89-implementation-plan.md), [NEO-90](../../plans/NEO-90-implementation-plan.md), [NEO-91](../../plans/NEO-91-implementation-plan.md), [NEO-92](../../plans/NEO-92-implementation-plan.md), [NEO-93](../../plans/NEO-93-implementation-plan.md), [NEO-94](../../plans/NEO-94-implementation-plan.md), [NEO-95](../../plans/NEO-95-implementation-plan.md), [NEO-96](../../plans/NEO-96-implementation-plan.md), [NEO-97](../../plans/NEO-97-implementation-plan.md), [NEO-98](../../plans/NEO-98-implementation-plan.md), label **`E5.M2`** on NEO-87–NEO-98 | +| E5.M3 | In Progress | **Backlog decomposed:** Epic 5 Slice 3 — [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md) **E5M3-01** [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) → **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111). Prototype spine: one encounter **`prototype_combat_pocket`** (defeat all three E5.M2 NPC ids) → reward table **`prototype_combat_pocket_clear`** (**`scrap_metal_bulk` ×10**, **`contract_handoff_token` ×1**); idempotent completion; per-defeat gig XP unchanged ([NEO-44](../../plans/NEO-44-implementation-plan.md)). **Implementation not started.** Upstream **E5.M2 Ready**, **E3.M3** inventory landed. | [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md), [E5_M3](E5_M3_EncounterAndRewardTables.md), label **`E5.M3`** on NEO-100–NEO-111 | --- diff --git a/docs/decomposition/modules/module_dependency_register.md b/docs/decomposition/modules/module_dependency_register.md index 3e076cc..2dc8eef 100644 --- a/docs/decomposition/modules/module_dependency_register.md +++ b/docs/decomposition/modules/module_dependency_register.md @@ -73,13 +73,15 @@ Fleshed-out scope, contracts, and integration notes live in **per-module documen |---|---|---|---|---|---| | E5.M1 | CombatRulesEngine | E1.M3, E2.M2 | CombatAction, CombatResolution, ThreatState | Prototype | Ready | | E5.M2 | NpcAiAndBehaviorProfiles | E5.M1 | NpcBehaviorDef, TelegraphEvent, AggroRule | Prototype | In Progress | -| E5.M3 | EncounterAndRewardTables | E5.M1, E3.M3, E7.M2 | EncounterDef, RewardTable, EncounterCompleteEvent | Prototype | Planned | +| E5.M3 | EncounterAndRewardTables | E5.M1, E5.M2, E3.M3, E7.M2 | EncounterDef, RewardTable, EncounterCompleteEvent | Prototype | In Progress | | E5.M4 | GroupCombatScaling | E5.M3, E8.M1 | ScalingProfile, PartySizeModifier, CombatDifficultyBand | Pre-production | Planned | **E5.M1 note:** Epic 5 **Slice 1** backlog in Linear ([Epic 5 — PvE Combat and Encounter Content](https://linear.app/neon-sprawl/project/epic-5-pve-combat-and-encounter-content-cf3c94c3-5346-40e0-8aaf-281e846f2846)): [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) → [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); label **`E5.M1`**. Gig XP on defeat: **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44). See [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md), [E5_M1_CombatRulesEngine.md](E5_M1_CombatRulesEngine.md). Builds on E1.M4 cast funnel (NEO-28/31/32); extends **`POST …/ability-cast`** with **`CombatResolution`**. **NEO-76 landed:** frozen four-ability catalog + CI ([NEO-76 plan](../../plans/NEO-76-implementation-plan.md)). **NEO-77 landed:** fail-fast server load of `content/abilities/*_abilities.json` ([NEO-77 plan](../../plans/NEO-77-implementation-plan.md)). **NEO-79 landed:** injectable **`IAbilityDefinitionRegistry`** + DI; hotbar/cast use **`TryNormalizeKnown`** ([NEO-79 plan](../../plans/NEO-79-implementation-plan.md)). **NEO-78 landed:** **`GET /game/world/ability-definitions`** — `AbilityDefinitionsWorldApi` + DTOs in `Game/Combat/` ([NEO-78 plan](../../plans/NEO-78-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/ability-definitions/`. **NEO-80 landed:** **`ICombatEntityHealthStore`** / **`InMemoryCombatEntityHealthStore`** — lazy init (Slice 1 flat 100 HP; **superseded by [NEO-91 plan](../../plans/NEO-91-implementation-plan.md)** per-archetype catalog max HP + three NPC instance ids); DI via **`AddCombatEntityHealthStore()`** ([NEO-80 plan](../../plans/NEO-80-implementation-plan.md)); [server README — Combat entity health (NEO-80, NEO-91)](../../../server/README.md#combat-entity-health-neo-80-neo-91). **NEO-81 landed:** **`CombatOperations.TryResolve`** + **`CombatResult`** + **`CombatReasonCodes`** — defeated pre-check deny vocabulary (`target_defeated`, `unknown_ability`, `unknown_target`) ([NEO-81 plan](../../plans/NEO-81-implementation-plan.md)); [server README — Combat engine (NEO-81)](../../../server/README.md#combat-engine-neo-81). **NEO-82 landed:** **`AbilityCastApi`** invokes **`CombatOperations.TryResolve`** after E1.M4 gates; nested wire **`combatResolution`** on accept; per-ability catalog **`cooldownSeconds`** on successful resolve; **`target_defeated`** JSON cast deny without cooldown ([NEO-82 plan](../../plans/NEO-82-implementation-plan.md)); [server README — Ability cast (NEO-82)](../../../server/README.md#ability-cast-neo-31-neo-82); Bruno `bruno/neon-sprawl-server/ability-cast/` defeat spine. **NEO-83 landed:** **`GET /game/world/combat-targets`** — `CombatTargetsWorldApi` + DTOs; authoritative HP snapshot from **`ICombatEntityHealthStore`** for client HUD ([NEO-83 plan](../../plans/NEO-83-implementation-plan.md)); [server README — Combat targets snapshot (NEO-83)](../../../server/README.md#combat-targets-snapshot-neo-83); Bruno `bruno/neon-sprawl-server/combat-targets/`. **NEO-44 landed:** **`CombatDefeatGigXpGrant`** on cast **`targetDefeated`** — **25** gig XP to **`breach`** via **`IPlayerGigProgressionStore`** (V007); **`GET …/gig-progression`** ([NEO-44 plan](../../plans/NEO-44-implementation-plan.md)); [server README — Gig progression (NEO-44)](../../../server/README.md#gig-progression-snapshot-neo-44). **NEO-84 landed:** comment-only **`ability_used`** / **`enemy_defeat`** telemetry hook sites in **`CombatOperations.TryResolve`**; reserved **`encounter_start`** / **`player_death`** ([NEO-84 plan](../../plans/NEO-84-implementation-plan.md)); [server README — Combat telemetry hooks (NEO-84)](../../../server/README.md#combat-telemetry-hooks-neo-84). **NEO-85 landed:** client combat HUD — **`combat_targets_client.gd`**, **`CombatTargetHpLabel`**, **`CastFeedbackLabel`** resolution copy; event-driven **`GET /game/world/combat-targets`** refresh ([NEO-85 plan](../../plans/NEO-85-implementation-plan.md), [`NEO-85` manual QA](../../manual-qa/NEO-85.md)); [client README — Combat feedback + target HP HUD (NEO-85)](../../../client/README.md#combat-feedback--target-hp-hud-neo-85). **NEO-86 landed:** playable combat capstone — **`gig_progression_client.gd`**, **`GigXpLabel`**, defeat-triggered gig GET refresh; capstone manual QA [`NEO-86`](../../manual-qa/NEO-86.md) ([NEO-86 plan](../../plans/NEO-86-implementation-plan.md)); [client README — End-to-end combat loop (NEO-86)](../../../client/README.md#end-to-end-combat-loop-neo-86). **Epic 5 Slice 1 complete — register row Ready.** **E5.M2 note:** Epic 5 **Slice 2** backlog in Linear ([Epic 5 — PvE Combat and Encounter Content](https://linear.app/neon-sprawl/project/epic-5-pve-combat-and-encounter-content-cf3c94c3-5346-40e0-8aaf-281e846f2846)): [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) → [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98); label **`E5.M2`**. Client capstone **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98). See [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md), [E5_M2_NpcAiAndBehaviorProfiles.md](E5_M2_NpcAiAndBehaviorProfiles.md). Upstream **E5.M1 Ready**. **NEO-87 landed:** frozen three-behavior catalog + CI ([NEO-87 plan](../../plans/NEO-87-implementation-plan.md)). **NEO-88 landed:** fail-fast server load of `content/npc-behaviors/*_npc_behaviors.json` ([NEO-88 plan](../../plans/NEO-88-implementation-plan.md)); [server README — NPC behavior catalog](../../../server/README.md#npc-behavior-catalog-contentnpc-behaviors-neo-88). **NEO-89 landed:** injectable **`INpcBehaviorDefinitionRegistry`** + DI ([NEO-89 plan](../../plans/NEO-89-implementation-plan.md)). **NEO-90 landed:** **`GET /game/world/npc-behavior-definitions`** — `NpcBehaviorDefinitionsWorldApi` + DTOs in `Game/Npc/` ([NEO-90 plan](../../plans/NEO-90-implementation-plan.md)); [server README — NPC behavior definitions (NEO-90)](../../../server/README.md#npc-behavior-definitions-neo-90); Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`. **NEO-91 landed:** **`PrototypeNpcRegistry`** + combat-target migration — three NPC instance ids, per-archetype catalog max HP ([NEO-91 plan](../../plans/NEO-91-implementation-plan.md)); [server README — Combat entity health (NEO-91)](../../../server/README.md#combat-entity-health-neo-80-neo-91). **NEO-92–NEO-96 landed:** aggro/threat, runtime state machine, **`GET …/npc-runtime-snapshot`**, player combat HP + NPC attack resolve, telemetry hooks ([NEO-92](../../plans/NEO-92-implementation-plan.md)–[NEO-96](../../plans/NEO-96-implementation-plan.md)). **NEO-97 landed:** client telegraph HUD ([NEO-97 plan](../../plans/NEO-97-implementation-plan.md), [`NEO-97` manual QA](../../manual-qa/NEO-97.md)). **NEO-98 landed:** playable NPC telegraph combat capstone ([NEO-98 plan](../../plans/NEO-98-implementation-plan.md), [`NEO-98` manual QA](../../manual-qa/NEO-98.md)); [client README — End-to-end NPC telegraph combat loop (NEO-98)](../../../client/README.md#end-to-end-npc-telegraph-combat-loop-neo-98). **Epic 5 Slice 2 complete — register row Ready.** Replaces **`prototype_target_alpha` / `beta`** with three NPC archetype instances; owns **`ThreatState`**, **`TelegraphEvent`**, session **`IPlayerCombatHealthStore`**. +**E5.M3 note:** Epic 5 **Slice 3** backlog in Linear ([Epic 5 — PvE Combat and Encounter Content](https://linear.app/neon-sprawl/project/epic-5-pve-combat-and-encounter-content-cf3c94c3-5346-40e0-8aaf-281e846f2846)): [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) → [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111); label **`E5.M3`**. Client capstone **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111). See [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md), [E5_M3_EncounterAndRewardTables.md](E5_M3_EncounterAndRewardTables.md). Upstream **E5.M2 Ready**, **E3.M3** inventory landed. Prototype spine: one encounter **`prototype_combat_pocket`** (defeat all three E5.M2 NPC ids) → reward table **`prototype_combat_pocket_clear`** ( **`scrap_metal_bulk` ×10**, **`contract_handoff_token` ×1** ); idempotent completion per player; per-defeat gig XP unchanged ([NEO-44](../../plans/NEO-44-implementation-plan.md)). **Backlog decomposed** — implementation not started. + ### Epic 6 — PvP Security | Module ID | Module Name | Depends On | Contract Needed | Phase Required | Status | diff --git a/docs/plans/E5M2-prototype-backlog.md b/docs/plans/E5M2-prototype-backlog.md index 09c77bb..9c0e01c 100644 --- a/docs/plans/E5M2-prototype-backlog.md +++ b/docs/plans/E5M2-prototype-backlog.md @@ -388,7 +388,7 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d ## After this backlog -- **[E5.M3](../decomposition/modules/E5_M3_EncounterAndRewardTables.md)** Slice 3 — decompose when E5.M2 lands (encounters + loot routing). +- **[E5.M3](../decomposition/modules/E5_M3_EncounterAndRewardTables.md)** Slice 3 — decomposed in [E5M3-prototype-backlog.md](E5M3-prototype-backlog.md) (**NEO-100** → **NEO-111**). - Track delivery in Linear; keep `blockedBy` synchronized if scope changes. - For each issue kickoff: `docs/plans/{NEO-XX}-implementation-plan.md` per [story-kickoff](../../.cursor/rules/story-kickoff.md). - Add `docs/manual-qa/{NEO-XX}.md` for player-visible **client** stories when implementation lands. diff --git a/docs/plans/E5M3-prototype-backlog.md b/docs/plans/E5M3-prototype-backlog.md new file mode 100644 index 0000000..d022aba --- /dev/null +++ b/docs/plans/E5M3-prototype-backlog.md @@ -0,0 +1,375 @@ +# E5.M3 — Prototype story backlog (EncounterAndRewardTables) + +Working backlog for **Epic 5 — Slice 3** ([encounters and loot routing](../decomposition/epics/epic_05_pve_combat.md#slice-3---encounters-and-loot-routing)). Decomposition and contracts: [E5.M3 — EncounterAndRewardTables](../decomposition/modules/E5_M3_EncounterAndRewardTables.md). + +**Full-stack policy:** Epics deliver **playable game features**. Every **player-visible** story has a **`client`** Linear issue created in **this same decomposition pass** — not an undocumented follow-up. Reference: [E5M2 (paired server+client)](../plans/E5M2-prototype-backlog.md). + +**Labels (Linear):** every issue **`E5.M3`**; add **`server`** or **`client`** + **`Story`** as listed. + +**Precursor (do not re-scope):** [E5.M2](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) NPC combat pocket — three NPC instance ids (**`prototype_npc_melee`**, **`prototype_npc_ranged`**, **`prototype_npc_elite`**) with defeat via **`CombatOperations`** ([NEO-82](https://linear.app/neon-sprawl/issue/NEO-82)). Per-defeat **gig XP** stays on [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44) — encounter complete adds **loot + quest-credit stand-in**, not a second gig XP bundle. + +**Upstream (must be landed):** [E5.M2](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) **Ready**; [E3.M3](../decomposition/modules/E3_M3_ItemizationAndInventorySchema.md) inventory **`PlayerInventoryOperations`** ([NEO-54](https://linear.app/neon-sprawl/issue/NEO-54)–[NEO-55](https://linear.app/neon-sprawl/issue/NEO-55)). **E7.M2** full **`QuestRewardBundle`** router is **not** required — prototype uses **`contract_handoff_token`** loot + **`EncounterCompleteEvent`** hook for future quest credit ([NEO-43](https://linear.app/neon-sprawl/issue/NEO-43) pattern). + +**Prototype encounter spine (frozen in E5M3-01):** **one** `EncounterDef` — id **`prototype_combat_pocket`** — completion **`defeat_all_targets`** over the three E5.M2 NPC instance ids. **One** `RewardTable` — id **`prototype_combat_pocket_clear`** — deterministic fixed item grants (no RNG). + +**Linear issues (created):** attach `docs/plans/NEO-*-implementation-plan.md` on the story branch when work starts. + +| Slug | Layer | Linear | +|------|-------|--------| +| E5M3-01 | server | [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) | +| E5M3-02 | server | [NEO-101](https://linear.app/neon-sprawl/issue/NEO-101) | +| E5M3-03 | server | [NEO-102](https://linear.app/neon-sprawl/issue/NEO-102) | +| E5M3-04 | server | [NEO-103](https://linear.app/neon-sprawl/issue/NEO-103) | +| E5M3-05 | server | [NEO-104](https://linear.app/neon-sprawl/issue/NEO-104) | +| E5M3-06 | server | [NEO-105](https://linear.app/neon-sprawl/issue/NEO-105) | +| E5M3-07 | server | [NEO-106](https://linear.app/neon-sprawl/issue/NEO-106) | +| E5M3-08 | server | [NEO-108](https://linear.app/neon-sprawl/issue/NEO-108) | +| E5M3-09 | server | [NEO-107](https://linear.app/neon-sprawl/issue/NEO-107) | +| E5M3-10 | server | [NEO-109](https://linear.app/neon-sprawl/issue/NEO-109) | +| E5M3-11 | client | [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) | +| E5M3-12 | client | [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) | + +**Dependency graph in Linear:** E5M3-02 blocked by E5M3-01. E5M3-03 blocked by E5M3-02. E5M3-04 blocked by E5M3-03. E5M3-05 blocked by E5M3-04. E5M3-06 blocked by E5M3-05. E5M3-07 blocked by E5M3-06. E5M3-08 blocked by E5M3-07. E5M3-09 blocked by E5M3-06 (may parallel E5M3-08). E5M3-10 blocked by E5M3-07. E5M3-11 blocked by E5M3-08 and E5M3-09. E5M3-12 blocked by E5M3-11. *(Linear ids: E5M3-08 = **NEO-108**, E5M3-09 = **NEO-107** — creation order swap.)* + +**Board order:** estimates **1–12** matching slug order (E5M3-01 = 1 … E5M3-12 = 12). On the Epic 5 project board, sort by **Estimate** (ascending). + +--- + +## Story order (recommended) + +| Order | Slug | Layer | Depends on | +|-------|------|-------|------------| +| 1 | **E5M3-01** | server | E5.M2 Ready, E3.M3 inventory landed | +| 2 | **E5M3-02** | server | E5M3-01 | +| 3 | **E5M3-03** | server | E5M3-02 | +| 4 | **E5M3-04** | server | E5M3-03 | +| 5 | **E5M3-05** | server | E5M3-04 | +| 6 | **E5M3-06** | server | E5M3-05 | +| 7 | **E5M3-07** | server | E5M3-06 | +| 8 | **E5M3-08** | server | E5M3-07 | +| 9 | **E5M3-09** | server | E5M3-06 | +| 10 | **E5M3-10** | server | E5M3-07 | +| 11 | **E5M3-11** | client | E5M3-08, E5M3-09 | +| 12 | **E5M3-12** | client | E5M3-11 | + +**Downstream (separate modules):** [E5.M4](../decomposition/modules/E5_M4_GroupCombatScaling.md) group scaling; [E7.M1](../decomposition/modules/E7_M1_QuestStateMachine.md) / [E7.M2](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md) consume **`EncounterCompleteEvent`**; [E4.M2](../decomposition/modules/E4_M2_SpawnEcologyController.md) spawn ecology. + +--- + +## Kickoff decisions (decomposition defaults) + +| Topic | Decision | Rationale | +|-------|----------|-----------| +| Encounter count | **1** frozen `EncounterDef` | Vision prototype minimum; epic Slice 3 | +| Completion rule | **`defeat_all_targets`** over three E5.M2 NPC ids | Reuses static registry; order-independent | +| Reward table | **Deterministic fixed grants** (no RNG rolls) | Readable tests; RNG is pre-production | +| Frozen loot | **`scrap_metal_bulk` ×10**, **`contract_handoff_token` ×1** | Material + quest-token stand-in for E7.M2 credit | +| Idempotency | **Once per player per `encounterId`** | Slice 3 AC: grants expected payout **once** | +| Gig XP | **Keep [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44) per-defeat** | Combat → gig XP is separate from encounter loot bundle | +| Quest credit | **`contract_handoff_token`** item + **`EncounterCompleteEvent`** record | Full E7.M2 router deferred; visible in inventory HUD | +| Encounter start | **Auto** on first damaging cast vs any required NPC | Activates progress tracking; **`encounter_start`** hook site | +| Progress store | **In-memory** **`IEncounterProgressStore`** + **`IEncounterCompletionStore`** | Session prototype; Postgres optional follow-on | +| HTTP read | **`GET /game/players/{id}/encounter-progress`** | Client HUD without trusting local defeat math | +| HUD fidelity | Prototype Labels / debug panels (not final art) | NEO-110 precedent | + +--- + +### E5M3-01 — Prototype EncounterDef + RewardTable catalog + schemas + CI + +**Goal:** Lock content shape and CI validation for **one** frozen encounter + **one** reward table before server load. + +**In scope** + +- `content/schemas/encounter-def.schema.json`, `content/schemas/reward-table.schema.json`, `content/schemas/reward-grant-row.schema.json`. +- `content/encounters/prototype_encounters.json` — stable **`id`**: **`prototype_combat_pocket`**; **`completionCriteria.kind`**: **`defeat_all_targets`**; **`requiredNpcInstanceIds`**: three E5.M2 NPC ids; **`rewardTableId`**: **`prototype_combat_pocket_clear`**. +- `content/reward-tables/prototype_reward_tables.json` — stable **`id`**: **`prototype_combat_pocket_clear`**; **`fixedGrants`**: **`scrap_metal_bulk` ×10**, **`contract_handoff_token` ×1**. +- `scripts/validate_content.py`: schema validation, duplicate ids, exact one-encounter / one-table allowlist, cross-ref **`itemId`** → frozen item catalog, **`requiredNpcInstanceIds`** → frozen NPC instance ids, **`rewardTableId`** → reward table ids. +- Designer note in [E5_M3](../decomposition/modules/E5_M3_EncounterAndRewardTables.md) + `content/README.md`. + +**Out of scope** + +- Server loader, runtime engine, HTTP, Godot. + +**Acceptance criteria** + +- [ ] PR gate validates encounter + reward-table JSON against schema. +- [ ] Exactly one prototype encounter id and one reward table id; duplicate `id` fails CI. +- [ ] Stable id list documented in module doc freeze box. + +--- + +### E5M3-02 — Server encounter + reward catalog load (fail-fast) + +**Goal:** Disk → host: startup load of `content/encounters/*.json` and `content/reward-tables/*.json` with CI-parity validation. + +**In scope** + +- Loader + catalog types under `server/NeonSprawl.Server/Game/Encounters/` (path finalized in plan). +- Fail-fast on malformed files, duplicate ids, allowlist mismatch with CI, broken cross-refs. +- Unit tests (AAA) for loader happy path and failure modes. +- `server/README.md` section. + +**Out of scope** + +- Injectable registry interface (E5M3-03), HTTP projection, runtime completion. + +**Acceptance criteria** + +- [ ] Host fails startup on invalid encounter or reward-table JSON (mirror CI rules). +- [ ] Tests cover at least one happy path and one malformed catalog rejection. + +--- + +### E5M3-03 — Injectable encounter/reward registries + DI + +**Goal:** Replace direct catalog access with injectable registries; wire into host DI. + +**In scope** + +- `IEncounterDefinitionRegistry`, `IRewardTableDefinitionRegistry` + implementations backed by E5M3-02 catalogs. +- `TryNormalizeKnown` / list-in-id-order parity with ability/item registry patterns. +- DI extension method; unit tests (AAA). + +**Out of scope** + +- HTTP routes, progress store, client. + +**Acceptance criteria** + +- [ ] Registries resolve frozen encounter + reward table ids. +- [ ] Unknown id normalization fails closed. + +--- + +### E5M3-04 — GET `/game/world/encounter-definitions` + +**Goal:** Read-only world projection of encounter defs (+ nested or linked reward table summary) for client labels and Bruno smokes. + +**In scope** + +- `EncounterDefinitionsWorldApi` + versioned DTOs in `Game/Encounters/`. +- Integration tests (AAA); Bruno folder `bruno/neon-sprawl-server/encounter-definitions/`. +- `server/README.md` route section. + +**Out of scope** + +- Per-player progress, completion grants, Godot. + +**Acceptance criteria** + +- [ ] GET returns frozen encounter in ascending `id` order with `schemaVersion` **1**. +- [ ] Bruno happy GET passes in CI Bruno step. + +--- + +### E5M3-05 — Encounter progress + completion stores + +**Goal:** Server-owned per-player encounter activation, defeat progress, and idempotent completion flag. + +**In scope** + +- `IEncounterProgressStore` — tracks **`started`**, defeated subset of **`requiredNpcInstanceIds`**, keyed by **`(playerId, encounterId)`**. +- `IEncounterCompletionStore` — records **`completedAt`** (or boolean) once rewards applied; replays are no-op. +- `EncounterProgressOperations.TryMarkTargetDefeated` / `TryActivateOnFirstEngagement` (first damaging cast vs required NPC). +- Unit tests (AAA) with injected stores. + +**Out of scope** + +- Inventory grants (E5M3-06), HTTP DTOs (E5M3-08), Godot. + +**Acceptance criteria** + +- [ ] Defeating required NPCs accumulates progress in any order. +- [ ] Completion predicate true only when all required ids defeated. +- [ ] Second completion attempt on same player+encounter is idempotent no-op at store layer. + +--- + +### E5M3-06 — EncounterCompletionOperations + inventory reward apply + +**Goal:** On first completion, apply **`RewardTable.fixedGrants`** via **`PlayerInventoryOperations`**; emit internal **`EncounterCompleteEvent`** payload for downstream routers. + +**In scope** + +- `EncounterCompletionOperations.TryCompleteAndGrant` — checks completion store, applies fixed grants, marks complete atomically (best-effort rollback on inventory deny documented in plan). +- Structured deny reason codes (`inventory_full`, `already_completed`, `not_ready`, …). +- Integration tests: defeat all targets → inventory stacks increase exactly once. +- Bruno spine optional in plan (may land in E5M3-08). + +**Out of scope** + +- Godot HUD — **client counterpart [NEO-110](#)**. +- Full E7.M2 quest router (E5M3-09 hook stub). + +**Acceptance criteria** + +- [ ] First completion grants **`scrap_metal_bulk` ×10** and **`contract_handoff_token` ×1** deterministically. +- [ ] Second completion does not duplicate grants. +- [ ] Inventory deny fails closed without marking complete. + +**Client counterpart:** [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) — encounter progress + loot feedback labels. + +--- + +### E5M3-07 — Wire combat defeat → encounter progress/completion + +**Goal:** Hook **`AbilityCastApi`** (or shared combat defeat helper) so NPC **`targetDefeated`** updates encounter progress and invokes E5M3-06 when the encounter completes. + +**In scope** + +- Call **`EncounterProgressOperations`** after successful player → NPC defeat ([NEO-82](https://linear.app/neon-sprawl/issue/NEO-82) path). +- **`TryActivateOnFirstEngagement`** on first damaging cast vs encounter NPC ( **`encounter_start`** comment hook site). +- Integration tests: Bruno or in-proc defeat-all-three → completion + inventory grant once. +- Preserve existing **NEO-44** gig XP on each defeat (do not remove). + +**Out of scope** + +- Godot presentation (E5M3-11). +- Quest router beyond completion event record (E5M3-09). + +**Acceptance criteria** + +- [ ] Defeating third required NPC triggers exactly one completion grant. +- [ ] Partial progress (1–2 defeats) does not grant loot. +- [ ] Gig XP still awarded per defeat via **`CombatDefeatGigXpGrant`**. + +--- + +### E5M3-08 — GET `/game/players/{id}/encounter-progress` + wire DTOs + +**Goal:** Authoritative per-player encounter rows for client poll / event refresh. + +**In scope** + +- `EncounterProgressApi` + DTOs: per encounter **`state`** (`inactive` / `active` / `completed`), **`defeatedTargetIds`**, **`completedAt`**, last **`rewardGrantSummary`** (item ids + quantities on complete). +- Integration tests (AAA); Bruno `bruno/neon-sprawl-server/encounter-progress/`. +- `server/README.md` section. + +**Out of scope** + +- Godot poll client (E5M3-11). + +**Acceptance criteria** + +- [ ] GET reflects progress after each defeat without client-side inference. +- [ ] Completed encounter shows **`completed`** state and grant summary. +- [ ] Bruno smoke: defeat chain → GET shows **`completed`** once. + +**Client counterpart:** [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) — poll/refresh from this GET. + +--- + +### E5M3-09 — EncounterCompleteEvent + E7.M2 quest-credit hook stub + +**Goal:** Persist **`EncounterCompleteEvent`** record and document **E7.M2** consumer hook for quest credit (beyond quest-token item loot). + +**In scope** + +- `EncounterCompleteEvent` internal type (playerId, encounterId, idempotencyKey, grantedItemSummary, completedAt). +- Comment-only **`reward_delivery`** / quest-credit hook site in completion path (TODO E7.M2). +- `server/README.md` pointer; module doc cross-link to [E7_M2](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md). + +**Out of scope** + +- Production quest state machine, Godot. + +**Acceptance criteria** + +- [ ] Completion path emits/records event exactly once per player+encounter. +- [ ] Hook documents E7.M2 **`QuestRewardBundle`** consumer contract. +- [ ] No runtime behavior beyond event record + comments. + +--- + +### E5M3-10 — Slice 3 encounter telemetry hook sites + +**Goal:** Comment-only **`encounter_complete`** and **`reward_attribution`** hook sites in encounter completion path (activates reserved **`encounter_start`** from E5M3-07). + +**In scope** + +- TODO(E9.M1) markers at encounter start, completion commit, and per-item grant attribution. +- `server/README.md` pointer; epic Slice 3 vocabulary cross-link. + +**Out of scope** + +- Production ingest, Godot. + +**Acceptance criteria** + +- [ ] Hook names match [epic_05 Slice 3](../decomposition/epics/epic_05_pve_combat.md#slice-3---encounters-and-loot-routing) vocabulary. +- [ ] No runtime behavior change beyond comments. + +--- + +### E5M3-11 — Client encounter progress + loot feedback HUD + +**Goal:** Godot shows **server-owned** encounter progress and completion loot using encounter-progress GET. + +**In scope** + +- `client/scripts/encounter_progress_client.gd` — poll or event-driven **`GET /game/players/{id}/encounter-progress`** after combat defeats. +- HUD labels: **`EncounterProgressLabel`**, **`EncounterCompleteLabel`** under `UICanvas`; refresh **`InventoryLabel`** on completion (reuse [NEO-72](https://linear.app/neon-sprawl/issue/NEO-72) client). +- GdUnit tests with HTTP doubles ([testing-expectations.md](../../.cursor/rules/testing-expectations.md)). +- `docs/manual-qa/NEO-110.md` — **Godot** steps (server + client running). +- `client/README.md` encounter section. + +**Out of scope** + +- Final loot VFX art; encounter selection UI beyond single frozen encounter. + +**Acceptance criteria** + +- [ ] Player-visible progress updates after each NPC defeat (e.g. **2/3** targets). +- [ ] Completion label shows grant summary matching server GET. +- [ ] Manual QA checklist exercisable without Bruno. + +**Client counterpart:** this issue (**NEO-110**). + +--- + +### E5M3-12 — Playable encounter clear capstone (Godot) + +**Goal:** Prove Epic 5 Slice 3 acceptance **in Godot**: defeat all three NPCs, receive loot + quest token **once**, verify inventory — without Bruno. + +**In scope** + +- `docs/manual-qa/NEO-111.md` single-session script (includes idempotency check — no duplicate loot on re-login / fresh defeat attempt after complete). +- `client/README.md` end-to-end encounter loop section. +- Update [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) E5.M3 row when complete. + +**Out of scope** + +- Group scaling ([E5.M4](../decomposition/modules/E5_M4_GroupCombatScaling.md)), dynamic spawn ([E4.M2](../decomposition/modules/E4_M2_SpawnEcologyController.md)). + +**Acceptance criteria** + +- [ ] Human completes script with server + client; loot readable in inventory HUD. +- [ ] Epic 5 Slice 3 AC: completing defined encounter grants expected items + quest token **once**. +- [ ] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums. + +**Client counterpart:** this issue (**NEO-111**). + +--- + +## After this backlog + +- Track delivery in Linear; keep `blockedBy` synchronized if scope changes. +- For each issue kickoff: `docs/plans/{NEO-XX}-implementation-plan.md` per [story-kickoff](../../.cursor/rules/story-kickoff.md). +- Add `docs/manual-qa/{NEO-XX}.md` for player-visible **client** stories when implementation lands. + +## Decomposition complete checklist + +- [x] Every player-visible AC has a **`client`** Linear issue (**NEO-110**, **NEO-111**) +- [x] No “optional follow-up” / “future client” without NEO-XX +- [x] Capstone client issue exists for playable Godot verification (**NEO-111**) +- [x] [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) updated (decomposed row) +- [x] [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) E5.M3 note updated when issues are created + +## Related docs + +- [E5_M3_EncounterAndRewardTables.md](../decomposition/modules/E5_M3_EncounterAndRewardTables.md) +- [E5_M2_NpcAiAndBehaviorProfiles.md](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) +- [E3_M3_ItemizationAndInventorySchema.md](../decomposition/modules/E3_M3_ItemizationAndInventorySchema.md) +- [E7_M2_RewardAndUnlockRouter.md](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md) +- [epic_05_pve_combat.md](../decomposition/epics/epic_05_pve_combat.md) +- [client_server_authority.md](../decomposition/modules/client_server_authority.md) From d7c3ede5a7dc7053aa67438a5e5656a3fdee33c8 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sat, 30 May 2026 19:25:56 -0400 Subject: [PATCH 2/2] chore: Epic 5 stable slice anchors and doc cluster alignment Add explicit HTML slice anchors to epic_05, restore deep links across E5 backlogs and plans, fix E5M2 NEO-98 resolve vocabulary and E5.M3 Linear row, and record review follow-up as Done. --- .../decomposition/epics/epic_05_pve_combat.md | 10 +++ .../modules/E5_M3_EncounterAndRewardTables.md | 2 +- docs/plans/E5M1-prototype-backlog.md | 6 +- docs/plans/E5M2-prototype-backlog.md | 14 ++-- docs/plans/E5M3-prototype-backlog.md | 6 +- docs/plans/NEO-84-implementation-plan.md | 2 +- docs/plans/NEO-86-implementation-plan.md | 2 +- docs/plans/NEO-96-implementation-plan.md | 2 +- docs/plans/NEO-98-implementation-plan.md | 2 +- .../2026-05-30-epic-5-anchor-stability.md | 77 +++++++++++++++++++ server/README.md | 4 +- 11 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 docs/reviews/2026-05-30-epic-5-anchor-stability.md diff --git a/docs/decomposition/epics/epic_05_pve_combat.md b/docs/decomposition/epics/epic_05_pve_combat.md index 910a200..619a1f7 100644 --- a/docs/decomposition/epics/epic_05_pve_combat.md +++ b/docs/decomposition/epics/epic_05_pve_combat.md @@ -46,6 +46,8 @@ Deliver tab-target combat resolution, NPC behaviors and telegraphs, encounter te ## Implementation Slices (Backlog-Ready) + + ### Slice 1 - Combat rules MVP - Scope: E5.M1 with target lock, basic attacks, 4-6 abilities, cooldowns/resources per prototype scope. @@ -56,6 +58,8 @@ Deliver tab-target combat resolution, NPC behaviors and telegraphs, encounter te **Linear backlog:** [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md) — [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) → [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); gig XP **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44). + + ### Slice 2 - NPC archetypes and telegraphs - Scope: E5.M2 for three archetypes: melee pressure, ranged control, elite mini-boss. @@ -66,6 +70,8 @@ Deliver tab-target combat resolution, NPC behaviors and telegraphs, encounter te **Linear backlog:** [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md) — [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) → [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98); client capstone **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98). Verify in **Godot**, not Bruno-only. + + ### Slice 3 - Encounters and loot routing - Scope: E5.M3 wired to E3.M3 and E7.M2 for rewards and quest progress. @@ -76,6 +82,8 @@ Deliver tab-target combat resolution, NPC behaviors and telegraphs, encounter te **Linear backlog:** [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md) — [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) → [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111); client capstone **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111). Verify in **Godot**, not Bruno-only. + + ### Slice 4 - Group scaling (pre-production) - Scope: E5.M4 with party-sized modifiers behind E8.M1. @@ -91,6 +99,8 @@ Deliver tab-target combat resolution, NPC behaviors and telegraphs, encounter te - Risk: Loot tables desync from item schema. - Mitigation: Shared validation in content pipeline; lint reward references. + + ## Definition of Done - Prototype combat, archetypes, and encounters meet documented minimums in master plan. diff --git a/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md b/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md index 41fc246..ce7f83e 100644 --- a/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md +++ b/docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md @@ -8,7 +8,7 @@ | **Epic** | [Epic 5 — PvE Combat](../epics/epic_05_pve_combat.md) | | **Stage target** | Prototype | | **Status** | In Progress — Slice 3 backlog [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md): **E5M3-01** [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) → **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) decomposed | -| **Linear** | Label **`E5.M3`** · [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md) **E5M3-01** [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) · **E5M3-02** [NEO-101](https://linear.app/neon-sprawl/issue/NEO-101) · **E5M3-03** [NEO-102](https://linear.app/neon-sprawl/issue/NEO-102) · **E5M3-04** [NEO-103](https://linear.app/neon-sprawl/issue/NEO-103) · **E5M3-05** [NEO-104](https://linear.app/neon-sprawl/issue/NEO-104) · **E5M3-06** [NEO-105](https://linear.app/neon-sprawl/issue/NEO-105) · **E5M3-07** [NEO-106](https://linear.app/neon-sprawl/issue/NEO-106) · **E5M3-08** [NEO-107](https://linear.app/neon-sprawl/issue/NEO-107) · **E5M3-09** [NEO-108](https://linear.app/neon-sprawl/issue/NEO-108) · **E5M3-10** [NEO-109](https://linear.app/neon-sprawl/issue/NEO-109) · **E5M3-11** [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) · **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) | +| **Linear** | Label **`E5.M3`** · [E5M3-prototype-backlog.md](../../plans/E5M3-prototype-backlog.md) **E5M3-01** [NEO-100](https://linear.app/neon-sprawl/issue/NEO-100) · **E5M3-02** [NEO-101](https://linear.app/neon-sprawl/issue/NEO-101) · **E5M3-03** [NEO-102](https://linear.app/neon-sprawl/issue/NEO-102) · **E5M3-04** [NEO-103](https://linear.app/neon-sprawl/issue/NEO-103) · **E5M3-05** [NEO-104](https://linear.app/neon-sprawl/issue/NEO-104) · **E5M3-06** [NEO-105](https://linear.app/neon-sprawl/issue/NEO-105) · **E5M3-07** [NEO-106](https://linear.app/neon-sprawl/issue/NEO-106) · **E5M3-08** [NEO-108](https://linear.app/neon-sprawl/issue/NEO-108) · **E5M3-09** [NEO-107](https://linear.app/neon-sprawl/issue/NEO-107) · **E5M3-10** [NEO-109](https://linear.app/neon-sprawl/issue/NEO-109) · **E5M3-11** [NEO-110](https://linear.app/neon-sprawl/issue/NEO-110) · **E5M3-12** [NEO-111](https://linear.app/neon-sprawl/issue/NEO-111) | ## Purpose diff --git a/docs/plans/E5M1-prototype-backlog.md b/docs/plans/E5M1-prototype-backlog.md index 08dd787..398e9e9 100644 --- a/docs/plans/E5M1-prototype-backlog.md +++ b/docs/plans/E5M1-prototype-backlog.md @@ -1,6 +1,6 @@ # E5.M1 — Prototype story backlog (CombatRulesEngine) -Working backlog for **Epic 5 — Slice 1** ([combat rules MVP](../decomposition/epics/epic_05_pve_combat.md#slice-1---combat-rules-mvp)). Decomposition and contracts: [E5.M1 — CombatRulesEngine](../decomposition/modules/E5_M1_CombatRulesEngine.md). +Working backlog for **Epic 5 — Slice 1** ([Epic 5 · Slice 1 — combat rules MVP](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-1)). Decomposition and contracts: [E5.M1 — CombatRulesEngine](../decomposition/modules/E5_M1_CombatRulesEngine.md). **Labels (Linear):** every issue **`E5.M1`**; add **`server`** or **`client`** + **`Story`** as listed. @@ -287,7 +287,7 @@ Working backlog for **Epic 5 — Slice 1** ([combat rules MVP](../decomposition/ **Acceptance criteria** -- [x] Hook names match [epic_05 Slice 1](../decomposition/epics/epic_05_pve_combat.md#slice-1---combat-rules-mvp) vocabulary. +- [x] Hook names match [epic_05 Slice 1](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-1) vocabulary. - [x] **`enemy_defeat`** site fires only on transition to defeated (not every overkill cast). **Landed ([NEO-84](https://linear.app/neon-sprawl/issue/NEO-84)):** comment-only **`ability_used`** / **`enemy_defeat`** hook sites in **`CombatOperations.TryResolve`**; reserved **`encounter_start`** / **`player_death`** ([NEO-84-implementation-plan.md](NEO-84-implementation-plan.md)); [server README — Combat telemetry hooks](../../../server/README.md#combat-telemetry-hooks-neo-84). @@ -368,7 +368,7 @@ Working backlog for **Epic 5 — Slice 1** ([combat rules MVP](../decomposition/ - [x] Human completes script with server + client; **`prototype_target_alpha`** shows defeated state in HUD. - [x] Epic 5 Slice 1 AC: combat readable in fixed isometric camera; no silent cast failures. -- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums. +- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#epic-5-definition-of-done) for prototype minimums. **Landed ([NEO-86](https://linear.app/neon-sprawl/issue/NEO-86)):** **`gig_progression_client.gd`** + **`GigXpLabel`**; defeat-triggered gig GET refresh; capstone manual QA [`NEO-86`](../manual-qa/NEO-86.md); plan [NEO-86-implementation-plan.md](NEO-86-implementation-plan.md); `client/README.md` end-to-end combat loop section. Epic 5 Slice 1 client capstone complete. diff --git a/docs/plans/E5M2-prototype-backlog.md b/docs/plans/E5M2-prototype-backlog.md index 9c0e01c..dc2d739 100644 --- a/docs/plans/E5M2-prototype-backlog.md +++ b/docs/plans/E5M2-prototype-backlog.md @@ -1,6 +1,6 @@ # E5.M2 — Prototype story backlog (NpcAiAndBehaviorProfiles) -Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../decomposition/epics/epic_05_pve_combat.md#slice-2---npc-archetypes-and-telegraphs)). Decomposition and contracts: [E5.M2 — NpcAiAndBehaviorProfiles](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md). +Working backlog for **Epic 5 — Slice 2** ([Epic 5 · Slice 2 — NPC archetypes and telegraphs](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-2)). Decomposition and contracts: [E5.M2 — NpcAiAndBehaviorProfiles](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md). **Full-stack policy:** Epics deliver **playable game features**. Every **player-visible** story has a **`client`** Linear issue created in **this same decomposition pass** — not an undocumented follow-up. Reference: [E5M1 (paired server+client)](../plans/E5M1-prototype-backlog.md). @@ -69,11 +69,13 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d | Player HP | **Session in-memory** **`IPlayerCombatHealthStore`** (100 default); **no Postgres** | NPC pressure readable in Godot without E5.M1 persistence scope | | NPC tick | **Lazy advance** on **`GET …/npc-runtime-snapshot`** poll (client ~1 Hz in combat pocket) | Mirrors cooldown snapshot poll ([NEO-32](https://linear.app/neon-sprawl/issue/NEO-32)) | | Telegraph wire | **`GET /game/world/npc-runtime-snapshot`** v1 — states + active **`TelegraphEvent`** rows | Single poll surface; no client-side windup math | -| NPC attacks | **Deterministic** damage from `NpcBehaviorDef.attackDamage` after telegraph resolves | Matches E5.M1 no-RNG combat | +| NPC attacks | **Deterministic** damage from behavior **`attackAbilityId`** → ability catalog **`baseDamage`** when holder is within ability **`maxRange`** after telegraph resolves | Matches E5.M1 no-RNG combat; behavior **`attackDamage`** = projection + CI cross-ref only ([NEO-98](https://linear.app/neon-sprawl/issue/NEO-98)) | | PvP | NPCs **never** acquire player targets blocked by E6 stub | [pvp_combat_integration.md](../decomposition/modules/pvp_combat_integration.md) | | World placement | Static **`PrototypeNpcRegistry`** (successor to target registry anchors) until [E4.M2](../decomposition/modules/E4_M2_SpawnEcologyController.md) | Same static-registry pattern as E5.M1 dummies | | HUD fidelity | Prototype Labels / debug panels (not final art) | NEO-85 / NEO-97 precedent | +**Resolve source ([NEO-98](https://linear.app/neon-sprawl/issue/NEO-98)):** telegraph complete uses **`attackAbilityId`** + NPC ability catalog (**`baseDamage`**, **`maxRange`**), not behavior **`attackDamage`**. **`attackDamage`** remains on the behavior row for HTTP projection and CI cross-ref only. + --- ### E5M2-01 — Prototype NpcBehaviorDef catalog + schemas + CI @@ -280,7 +282,7 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d ### E5M2-09 — NPC attack resolve + session player combat HP -**Goal:** When telegraph completes, apply **`attackDamage`** to aggro holder via **`IPlayerCombatHealthStore`**; expose player HP on snapshot or dedicated GET. +**Goal:** When telegraph completes, resolve via behavior **`attackAbilityId`** → ability catalog **`baseDamage`** when aggro holder is within ability **`maxRange`**; apply to holder via **`IPlayerCombatHealthStore`**; expose player HP on snapshot or dedicated GET. Behavior **`attackDamage`** is projection/CI cross-ref only (**[NEO-98](https://linear.app/neon-sprawl/issue/NEO-98)** superseded direct **`attackDamage`** resolve). **In scope** @@ -301,7 +303,7 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d - [x] Player HP read model matches store after NPC attack. - [x] No client-side damage math required for verification (Bruno or integration tests). -**Landed ([NEO-95](https://linear.app/neon-sprawl/issue/NEO-95)):** **`IPlayerCombatHealthStore`** + **`NpcAttackOperations.TryResolveTelegraphComplete`** — telegraph complete applies catalog damage; **`GET /game/players/{id}/combat-health`**; plan [NEO-95-implementation-plan.md](NEO-95-implementation-plan.md). +**Landed ([NEO-95](https://linear.app/neon-sprawl/issue/NEO-95)):** **`IPlayerCombatHealthStore`** + **`NpcAttackOperations.TryResolveTelegraphComplete`** — telegraph complete applies catalog damage; **`GET /game/players/{id}/combat-health`**; plan [NEO-95-implementation-plan.md](NEO-95-implementation-plan.md). **Resolve source superseded by [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98):** **`attackAbilityId`** + ability **`baseDamage`** / **`maxRange`** (behavior **`attackDamage`** = projection/CI cross-ref only). **Client counterpart:** [NEO-97](https://linear.app/neon-sprawl/issue/NEO-97) — player HP + incoming telegraph/attack feedback labels. @@ -322,7 +324,7 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d **Acceptance criteria** -- [x] Hook names match [epic_05 Slice 2](../decomposition/epics/epic_05_pve_combat.md#slice-2---npc-archetypes-and-telegraphs) vocabulary. +- [x] Hook names match [epic_05 Slice 2](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-2) vocabulary. - [x] No runtime behavior change beyond comments. **Landed ([NEO-96](https://linear.app/neon-sprawl/issue/NEO-96)):** comment-only **`telegraph_fired`** + **`npc_state_transition`** hook sites in **`NpcRuntimeOperations`**; plan [NEO-96-implementation-plan.md](NEO-96-implementation-plan.md). @@ -378,7 +380,7 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d - [x] Human completes script with server + client; telegraph timing readable before damage. - [x] Epic 5 Slice 2 AC: telegraph timing matches UI feedback; aggro rules feel deterministic in manual QA. - [x] All three archetypes defeated in one session; **`CombatTargetHpLabel`** shows defeated for each (manual QA steps 9, 14, 18). -- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums. +- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#epic-5-definition-of-done) for prototype minimums. **Landed ([NEO-98](https://linear.app/neon-sprawl/issue/NEO-98)):** playable NPC telegraph combat capstone — [`NEO-98` manual QA](../manual-qa/NEO-98.md); `client/README.md` end-to-end NPC telegraph combat loop section; plan [NEO-98-implementation-plan.md](NEO-98-implementation-plan.md). **Epic 5 Slice 2 client capstone complete.** diff --git a/docs/plans/E5M3-prototype-backlog.md b/docs/plans/E5M3-prototype-backlog.md index d022aba..da301ba 100644 --- a/docs/plans/E5M3-prototype-backlog.md +++ b/docs/plans/E5M3-prototype-backlog.md @@ -1,6 +1,6 @@ # E5.M3 — Prototype story backlog (EncounterAndRewardTables) -Working backlog for **Epic 5 — Slice 3** ([encounters and loot routing](../decomposition/epics/epic_05_pve_combat.md#slice-3---encounters-and-loot-routing)). Decomposition and contracts: [E5.M3 — EncounterAndRewardTables](../decomposition/modules/E5_M3_EncounterAndRewardTables.md). +Working backlog for **Epic 5 — Slice 3** ([Epic 5 · Slice 3 — encounters and loot routing](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-3)). Decomposition and contracts: [E5.M3 — EncounterAndRewardTables](../decomposition/modules/E5_M3_EncounterAndRewardTables.md). **Full-stack policy:** Epics deliver **playable game features**. Every **player-visible** story has a **`client`** Linear issue created in **this same decomposition pass** — not an undocumented follow-up. Reference: [E5M2 (paired server+client)](../plans/E5M2-prototype-backlog.md). @@ -296,7 +296,7 @@ Working backlog for **Epic 5 — Slice 3** ([encounters and loot routing](../dec **Acceptance criteria** -- [ ] Hook names match [epic_05 Slice 3](../decomposition/epics/epic_05_pve_combat.md#slice-3---encounters-and-loot-routing) vocabulary. +- [ ] Hook names match [epic_05 Slice 3](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-3) vocabulary. - [ ] No runtime behavior change beyond comments. --- @@ -345,7 +345,7 @@ Working backlog for **Epic 5 — Slice 3** ([encounters and loot routing](../dec - [ ] Human completes script with server + client; loot readable in inventory HUD. - [ ] Epic 5 Slice 3 AC: completing defined encounter grants expected items + quest token **once**. -- [ ] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums. +- [ ] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#epic-5-definition-of-done) for prototype minimums. **Client counterpart:** this issue (**NEO-111**). diff --git a/docs/plans/NEO-84-implementation-plan.md b/docs/plans/NEO-84-implementation-plan.md index 38a01b3..e28dd89 100644 --- a/docs/plans/NEO-84-implementation-plan.md +++ b/docs/plans/NEO-84-implementation-plan.md @@ -44,7 +44,7 @@ ## Acceptance criteria checklist -- [x] Hook names match [epic_05 Slice 1](../decomposition/epics/epic_05_pve_combat.md#slice-1---combat-rules-mvp) vocabulary (`ability_used`, `enemy_defeat`, reserved `encounter_start`, `player_death`). +- [x] Hook names match [epic_05 Slice 1](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-1) vocabulary (`ability_used`, `enemy_defeat`, reserved `encounter_start`, `player_death`). - [x] **`enemy_defeat`** site documents emit only on transition to defeated (not re-hit / overkill — enforced by `target_defeated` deny before damage). ## Technical approach diff --git a/docs/plans/NEO-86-implementation-plan.md b/docs/plans/NEO-86-implementation-plan.md index bfaac71..c0918c6 100644 --- a/docs/plans/NEO-86-implementation-plan.md +++ b/docs/plans/NEO-86-implementation-plan.md @@ -48,7 +48,7 @@ No other blocking decisions — combat loop HUD, hotbar pulse bootstrap, and eve - [x] Human completes **`docs/manual-qa/NEO-86.md`** with server + client; **`prototype_target_alpha`** shows defeated state in HUD. - [x] Epic 5 Slice 1 AC: combat readable in fixed isometric camera; no silent cast failures on deny. - [x] **`breach`** gig XP visible in Godot after defeat (≥ **25** from fresh-server baseline). -- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums. +- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#epic-5-definition-of-done) for prototype minimums. ## Implementation reconciliation (shipped) diff --git a/docs/plans/NEO-96-implementation-plan.md b/docs/plans/NEO-96-implementation-plan.md index bf091bb..785c651 100644 --- a/docs/plans/NEO-96-implementation-plan.md +++ b/docs/plans/NEO-96-implementation-plan.md @@ -46,7 +46,7 @@ ## Acceptance criteria checklist - [x] **`TODO(E9.M1)`** markers present at **`telegraph_fired`** and **`npc_state_transition`** hook sites in NPC runtime path. -- [x] Hook names match [epic_05 Slice 2](../decomposition/epics/epic_05_pve_combat.md#slice-2---npc-archetypes-and-telegraphs) vocabulary (`telegraph_fired`, `npc_state_transition`). +- [x] Hook names match [epic_05 Slice 2](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-2) vocabulary (`telegraph_fired`, `npc_state_transition`). - [x] No runtime behavior change beyond comments / doc cross-links. ## Implementation reconciliation (shipped) diff --git a/docs/plans/NEO-98-implementation-plan.md b/docs/plans/NEO-98-implementation-plan.md index 0c5d6b2..87a0aaa 100644 --- a/docs/plans/NEO-98-implementation-plan.md +++ b/docs/plans/NEO-98-implementation-plan.md @@ -50,7 +50,7 @@ - [x] Human completes **`docs/manual-qa/NEO-98.md`** with server + client; telegraph timing readable before damage on the melee spine. - [x] Epic 5 Slice 2 AC: telegraph timing matches UI feedback; aggro rules feel deterministic in manual QA. - [x] All three archetypes defeated in one session; **`CombatTargetHpLabel`** shows defeated for each. -- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums. +- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#epic-5-definition-of-done) for prototype minimums. ## Implementation reconciliation (shipped) diff --git a/docs/reviews/2026-05-30-epic-5-anchor-stability.md b/docs/reviews/2026-05-30-epic-5-anchor-stability.md new file mode 100644 index 0000000..8044f6f --- /dev/null +++ b/docs/reviews/2026-05-30-epic-5-anchor-stability.md @@ -0,0 +1,77 @@ +# Documentation review — Epic 5 anchor stability + doc cluster + +**Date:** 2026-05-30 +**Scope:** Uncommitted docs batch — `epic_05_pve_combat.md` stable slice anchors; link updates in `E5M1`/`E5M2`/`E5M3` backlogs, `NEO-84`/`NEO-86`/`NEO-96`/`NEO-98` plans, `server/README.md`; cross-check against Epic 5 modules, game-design progression seams, and full-stack backlog policy +**Base:** Working tree on `main` (uncommitted) + +## Verdict + +**Approve** — suggestions applied (2026-05-30 follow-up). + +## Summary + +The batch correctly adds explicit HTML slice anchors to `epic_05_pve_combat.md` (matching the Epic 1 / Epic 3 precedent) and removes fragile GitHub heading slugs (`#slice-1---combat-rules-mvp`, etc.) that were prone to renderer drift. Hook vocabulary, gig-vs-skill XP boundaries, and full-stack E5M1/E5M2/E5M3 backlog pairing remain aligned with game-design and module docs. Follow-ups from initial review (stable deep links, E5M2 NEO-98 resolve vocabulary, E5.M3 Linear row) are **Done.** + +## Documentation checked + +| Path | Result | +|------|--------| +| `docs/decomposition/epics/epic_05_pve_combat.md` | **Matches** — `` … `epic-5-slice-4`, `epic-5-definition-of-done`; slice telemetry vocabulary unchanged. | +| `docs/plans/E5M1-prototype-backlog.md` | **Matches** — epic links use stable `#epic-5-slice-1` / `#epic-5-definition-of-done` anchors; Slice 1 complete; full-stack checklist satisfied. | +| `docs/plans/E5M2-prototype-backlog.md` | **Matches** — stable slice/DoD anchors; kickoff + E5M2-09 document NEO-98 **`attackAbilityId`** resolve supersession; full-stack pairing **NEO-97**/**NEO-98** intact. | +| `docs/plans/E5M3-prototype-backlog.md` | **Matches** — stable `#epic-5-slice-3` / DoD anchors; decomposition checklist + client capstone **NEO-111** present. Gig XP / encounter loot separation matches `progression.md` seams. | +| `docs/plans/NEO-84-implementation-plan.md` | **Matches** — hook names align with epic Slice 1 list. | +| `docs/plans/NEO-86-implementation-plan.md` | **Matches** — capstone AC + gig XP proof; DoD link uses `#epic-5-definition-of-done`. | +| `docs/plans/NEO-96-implementation-plan.md` | **Matches** — Slice 2 hook vocabulary; reconciliation complete. | +| `docs/plans/NEO-98-implementation-plan.md` | **Matches** — capstone scope reconciliation documents NEO-98 server integration; aligns with [2026-05-30-NEO-98-followup.md](2026-05-30-NEO-98-followup.md). | +| `docs/decomposition/modules/E5_M1_CombatRulesEngine.md` | **Matches** — gig XP on defeat via NEO-44; combat → gig, not E2.M2 skill XP. | +| `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | **Matches** — freeze table + NEO-98 `attackAbilityId` / range-gated resolve documented. | +| `docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md` | **Matches** — slug table (lines 50–63) and summary **Linear** row both use **E5M3-08 → NEO-108**, **E5M3-09 → NEO-107**. | +| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **Matches** — E5.M1 Ready, E5.M2 Ready + NEO-98 integration, E5.M3 decomposed In Progress. | +| `docs/game-design/progression.md`, `gigs.md`, `abilities.md` | **N/A** (not edited) — spot-check: combat encounters → gig XP; no skill XP from fight loop reintroduced in Epic 5 docs. | +| `server/README.md` (NEO-84 combat hooks) | **Matches** — epic link valid; hook semantics unchanged. | +| `server/README.md` (NEO-96 NPC hooks) | **Matches** — epic cross-link to Slice 2 anchor added (parity with NEO-84). | + +## Blocking issues + +None. + +## Suggestions + +1. ~~**Use stable slice anchors in deep links** — Anchors exist (`epic-5-slice-1` … `epic-5-slice-4`, `epic-5-definition-of-done`) but updated links drop fragments entirely. Match Epic 3 / E3M1 pattern:~~ **Done.** Backlog intros, telemetry AC, DoD re-read AC, and `server/README.md` NEO-84/NEO-96 now use `#epic-5-slice-N` / `#epic-5-definition-of-done`. + + ~~Example target:~~ + + ```markdown + [Epic 5 · Slice 2 — NPC archetypes and telegraphs](../decomposition/epics/epic_05_pve_combat.md#epic-5-slice-2) + ``` + +2. ~~**E5M2 backlog — NEO-98 resolve source drift** — Kickoff table row **NPC attacks** and **E5M2-09** goal still say telegraph resolve applies `NpcBehaviorDef.attackDamage`. Module doc and NEO-98 plan correctly document **`attackAbilityId`** + ability **`baseDamage`** / **`maxRange`**. Add a kickoff footnote or revise the E5M2-09 goal + landed **NEO-95** line to note NEO-98 supersession (behavior **`attackDamage`** = projection/CI cross-ref only).~~ **Done.** + +3. ~~**E5_M3 module summary Linear row** — Line 11 lists **E5M3-08 → NEO-107**, **E5M3-09 → NEO-108**; backlog and slug table in the same module file use **E5M3-08 → NEO-108**, **E5M3-09 → NEO-107** (documented creation-order swap). Fix the summary **Linear** cell to match the slug table.~~ **Done.** + +## Nits + +- ~~**Related docs path style** — `E5M2`/`E5M3` backlogs use `./../decomposition/epics/epic_05_pve_combat.md`; `E5M1` uses `../decomposition/...`. Normalize to one form.~~ **Done.** All E5 backlogs use `../decomposition/...`. +- **Epic 5 Slice 4** — Anchor `epic-5-slice-4` added with no current consumers (pre-production); fine as forward-looking stability. +- **Prior reviews** — [2026-05-30-NEO-98-followup.md](2026-05-30-NEO-98-followup.md) suggestions are addressed; no reopen needed for NEO-98 doc cluster. + +## Verification + +After applying suggestions: + +```bash +# No remaining fragile epic_05 heading slugs +rg 'epic_05_pve_combat\.md#' docs/ server/ + +# Epic 5 stable anchors referenced where slice-specific +rg 'epic-5-slice-|epic-5-definition-of-done' docs/ + +# E5M3 slug ↔ Linear consistency +rg 'E5M3-0[89].*NEO-10[78]' docs/plans/E5M3-prototype-backlog.md docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md + +# NEO-98 resolve vocabulary in E5M2 backlog +rg 'attackDamage|attackAbilityId' docs/plans/E5M2-prototype-backlog.md +``` + +Human re-read: epic Slice 1–3 telemetry hook lists vs plan AC checklists; E5M3 kickoff **Gig XP** row vs `progression.md` combat → gig seam. diff --git a/server/README.md b/server/README.md index 1d6c5cf..ce42dab 100644 --- a/server/README.md +++ b/server/README.md @@ -192,7 +192,7 @@ Plan: [NEO-93 implementation plan](../../docs/plans/NEO-93-implementation-plan.m ### NPC runtime telemetry hooks (NEO-96) -Comment-only hook sites in **`NpcRuntimeOperations.AdvanceAll`** for future E9.M1 catalog events — **`telegraph_fired`** at each **`telegraph_windup`** entry (aggro→telegraph and recover→telegraph); **`npc_state_transition`** on every committed state change during **`AdvanceAll`** (including holder clear → **`idle`**). Dev fixture **`ResetAllPrototypeRows`** writes idle directly and bypasses **`AdvanceOne`** hooks (optional E9.M1 anchor noted on reset path). **`TODO(E9.M1)`** — no production ingest. Snapshot GET invokes **`AdvanceAll`** before read (no duplicate API-layer hooks). Plan: [NEO-96 implementation plan](../../docs/plans/NEO-96-implementation-plan.md). +Comment-only hook sites in **`NpcRuntimeOperations.AdvanceAll`** for future E9.M1 catalog events ([Epic 5 Slice 2](../../docs/decomposition/epics/epic_05_pve_combat.md#epic-5-slice-2)) — **`telegraph_fired`** at each **`telegraph_windup`** entry (aggro→telegraph and recover→telegraph); **`npc_state_transition`** on every committed state change during **`AdvanceAll`** (including holder clear → **`idle`**). Dev fixture **`ResetAllPrototypeRows`** writes idle directly and bypasses **`AdvanceOne`** hooks (optional E9.M1 anchor noted on reset path). **`TODO(E9.M1)`** — no production ingest. Snapshot GET invokes **`AdvanceAll`** before read (no duplicate API-layer hooks). Plan: [NEO-96 implementation plan](../../docs/plans/NEO-96-implementation-plan.md). ## NPC runtime snapshot (NEO-94) @@ -247,7 +247,7 @@ Plan: [NEO-81 implementation plan](../../docs/plans/NEO-81-implementation-plan.m ### Combat telemetry hooks (NEO-84) -Comment-only hook sites in **`CombatOperations.TryResolve`** for future E9.M1 catalog events ([Epic 5 Slice 1](../../docs/decomposition/epics/epic_05_pve_combat.md#slice-1---combat-rules-mvp)). **`TODO(E9.M1)`** — no production ingest. HTTP **`POST …/ability-cast`** calls the engine only (no duplicate API-layer hooks for these names). +Comment-only hook sites in **`CombatOperations.TryResolve`** for future E9.M1 catalog events ([Epic 5 Slice 1](../../docs/decomposition/epics/epic_05_pve_combat.md#epic-5-slice-1)). **`TODO(E9.M1)`** — no production ingest. HTTP **`POST …/ability-cast`** calls the engine only (no duplicate API-layer hooks for these names). - **`ability_used`** — on every successful resolve (zero-damage abilities included). - **`enemy_defeat`** — only when lethal damage drives **`after.Defeated`** to **`true`** (re-hit on defeated targets denies at **`target_defeated`** before damage).