# E3.M2 — Prototype story backlog (RefinementAndRecipeExecution) Working backlog for **Epic 3 — Slice 3** ([recipes and crafting pipeline](../decomposition/epics/epic_03_crafting_economy.md#epic-3-slice-3)). Decomposition and contracts: [E3.M2 — RefinementAndRecipeExecution](../decomposition/modules/E3_M2_RefinementAndRecipeExecution.md). **Labels (Linear):** every issue **`E3.M2`**; add **`server`** / **`Story`** as listed per issue. **Precursor (do not re-scope):** [NEO-42](https://linear.app/neon-sprawl/issue/NEO-42) ships **`RefineActivitySkillXpGrant.GrantOnSuccessfulCraftOrRefine`** — **10** **`refine`** XP with **`sourceKind: activity`** on successful craft/refine completion. Slice 3 **calls** that helper from the real **`CraftResult`** success path; NEO-42 does **not** own recipe validation, inventory mutation, or craft HTTP. **Upstream (must be landed):** [E3.M3](../decomposition/modules/E3_M3_ItemizationAndInventorySchema.md) Slice 1 — frozen item catalog, **`IPlayerInventoryStore`**, **`PlayerInventoryOperations`**, inventory HTTP ([NEO-50](https://linear.app/neon-sprawl/issue/NEO-50)–[NEO-56](https://linear.app/neon-sprawl/issue/NEO-56)). [E3.M1](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) Slice 2 — gather → **`scrap_metal_bulk`** ([NEO-57](https://linear.app/neon-sprawl/issue/NEO-57)–[NEO-64](https://linear.app/neon-sprawl/issue/NEO-64)). **E2.M2** grant path ([NEO-38](https://linear.app/neon-sprawl/issue/NEO-38)). **Prototype craft spine (frozen in E3M2-01):** **eight** `RecipeDef` rows proving **gather → process → make** on the frozen six-item catalog — **two** process (**refine**) recipes, **six** make recipes covering every **`prototypeRole`** output except **`material`** / **`intermediate`** (those are inputs). All recipes use **`requiredSkillId: refine`** (no separate **Make** skill row in the prototype skill trio). Station tags, skill level gates, and batch-quantity POST are **out of scope** for Slice 3. **Linear issues (created):** attach `docs/plans/NEO-*-implementation-plan.md` on the same branch as implementation work. | Slug | Linear | |------|--------| | E3M2-01 | [NEO-65](https://linear.app/neon-sprawl/issue/NEO-65) | | E3M2-02 | [NEO-66](https://linear.app/neon-sprawl/issue/NEO-66) | | E3M2-03 | [NEO-67](https://linear.app/neon-sprawl/issue/NEO-67) | | E3M2-04 | [NEO-68](https://linear.app/neon-sprawl/issue/NEO-68) | | E3M2-05 | [NEO-69](https://linear.app/neon-sprawl/issue/NEO-69) | | E3M2-06 | [NEO-70](https://linear.app/neon-sprawl/issue/NEO-70) | | E3M2-07 | [NEO-71](https://linear.app/neon-sprawl/issue/NEO-71) | **Dependency graph in Linear:** E3M2-02 (NEO-66) blocked by E3M2-01 (NEO-65). E3M2-03 (NEO-67) blocked by E3M2-02 (NEO-66). E3M2-04 (NEO-68) blocked by E3M2-03 (NEO-67). E3M2-05 (NEO-69) blocked by E3M2-03 (NEO-67) and E3.M3 inventory store (NEO-54). E3M2-06 (NEO-70) blocked by E3M2-05 (NEO-69). E3M2-07 (NEO-71) blocked by E3M2-05 (NEO-69; may parallel E3M2-06). **Board order:** each issue carries **estimate 1–7** matching slug order (E3M2-01 = 1 … E3M2-07 = 7). On the Epic 3 project board, use **Display options → Ordering → Estimate** (ascending) so Todo reads top-to-bottom in delivery order. --- ## Story order (recommended) | Order | Slug | Depends on | |-------|------|------------| | 1 | **E3M2-01** | E3.M3 item catalog (NEO-50) | | 2 | **E3M2-02** | E3M2-01 | | 3 | **E3M2-03** | E3M2-02 | | 4 | **E3M2-04** | E3M2-03 | | 5 | **E3M2-05** | E3M2-03, E3.M3 inventory store (NEO-54+) | | 6 | **E3M2-06** | E3M2-05 | | 7 | **E3M2-07** | E3M2-05 | **Downstream (separate modules):** [E7.M1](../decomposition/modules/E7_M1_QuestStateMachine.md) quest steps requiring crafted items; [E3.M4](../decomposition/modules/E3_M4_SinkAndDurabilityLifecycle.md) durability sinks; [E4.M1](../decomposition/modules/E4_M1_ZoneGraphAndTravelRules.md) bench/station placement later. --- ## Kickoff decisions (decomposition defaults) | Topic | Decision | Rationale | |-------|----------|-----------| | Recipe count | **8** frozen defs (epic minimum 8–12) | Two refine paths + six make outputs without expanding the item catalog | | Skill id | **`refine`** on every recipe | Prototype skill trio has no separate **Make** skill; matches [NEO-42](NEO-42-implementation-plan.md) | | Skill level gates | **None** in prototype | `requiredSkillLevel` omitted or **0**; gates are a content follow-up | | Station / bench tags | **None** in prototype | Craft always allowed via HTTP; zone/bench gating defers to [E4.M1](../decomposition/modules/E4_M1_ZoneGraphAndTravelRules.md) | | Craft wire surface | **`POST /game/players/{id}/craft`** | Mirror [NEO-55](https://linear.app/neon-sprawl/issue/NEO-55) inventory POST pattern; no station interact in Slice 3 | | Request shape | **`recipeId`** + optional **`quantity`** default **1** | One craft execution per POST; multi-output comes from recipe **`outputs`** | | Atomic commit | Consume **all** inputs, then grant **all** outputs, then XP | Roll back input removal if output grant fails (mirror gather engine rollback) | | XP | **`RefineActivitySkillXpGrant`** once per successful craft | NEO-42 already landed; E3M2-05 owns the call site | | RNG / quality / failure | **None** — deterministic success when inputs + bag space allow | Soft craft failure and quality tiers are pre-production ([crafting.md](../game-design/crafting.md)) | | Primary demo loop | Gather **`scrap_metal_bulk`** → **`refine_scrap_standard`** → **`make_field_stim_mk0`** | Proves combat-relevant consumable from world nodes | --- ### E3M2-01 — Prototype RecipeDef catalog + schemas + CI **Goal:** Lock content shape and CI validation for **eight** frozen recipes before server load. **In scope** - `content/schemas/recipe-def.schema.json` and `content/schemas/recipe-io-row.schema.json` (input/output row shape). - `content/recipes/prototype_recipes.json` with stable **`id`** values (see module doc freeze table). - `scripts/validate_content.py`: schema validation, duplicate ids, exact eight-recipe allowlist, **`recipeKind`** coverage (**`process`** + **`make`**), cross-check that every **`itemId`** in inputs/outputs exists in `content/items/`, every **`requiredSkillId`** exists in `content/skills/`. - Designer note in [E3_M2](../decomposition/modules/E3_M2_RefinementAndRecipeExecution.md) + `content/README.md`. **Out of scope** - Server loader, craft engine, HTTP. **Acceptance criteria** - [ ] PR gate validates recipe JSON against schema. - [ ] Exactly eight prototype recipe ids; duplicate `id` fails CI. - [ ] Every input/output references a valid frozen item id; every recipe references **`refine`**. - [ ] Stable id list documented in module doc freeze box. --- ### E3M2-02 — Server recipe catalog load (fail-fast) **Goal:** Disk → host: startup load of `content/recipes/*.json` with CI-parity validation. **In scope** - Loader + catalog types under `server/NeonSprawl.Server/Game/Crafting/` (path TBD in plan). - Fail-fast on malformed files, duplicate ids, unknown item ids (cross-check **`IItemDefinitionRegistry`**), unknown skill ids (cross-check **`ISkillDefinitionRegistry`**). - Unit tests (AAA) for loader happy path and failure modes. **Out of scope** - Per-player craft state, HTTP, inventory mutation. **Acceptance criteria** - [ ] Server refuses boot when recipe catalog invalid (mirror E3.M1 / E3.M3 catalog behavior). - [ ] Catalog resolves `RecipeDef` and I/O rows by recipe `id`. --- ### E3M2-03 — Recipe definition registry + DI **Goal:** Injectable `IRecipeDefinitionRegistry` consumed by craft engine and HTTP. **In scope** - `RecipeDefinitionRegistry` implementation + DI registration. - Unit tests (AAA): lookup, unknown id, input/output metadata, **`recipeKind`**. **Out of scope** - HTTP, persistence. **Acceptance criteria** - [ ] Host resolves registry from DI; unknown `recipeId` distinguishable from valid ids in tests. --- ### E3M2-04 — GET world recipe-definitions **Goal:** Versioned read-only catalog for Bruno, client preview, and tooling — mirror [NEO-53](https://linear.app/neon-sprawl/issue/NEO-53) item-definitions pattern. **In scope** - `GET /game/world/recipe-definitions` + DTOs + API tests (AAA). - Bruno folder `bruno/neon-sprawl-server/recipe-definitions/`. - `server/README.md` section. **Out of scope** - Per-player craft history or cooldown state. **Acceptance criteria** - [ ] GET returns all eight prototype recipes with schema version field and input/output summary fields needed for QA. - [ ] Bruno happy path documented. --- ### E3M2-05 — CraftResult engine (consume inputs, grant outputs, refine XP) **Goal:** Single server operation that resolves **`CraftResult`**: validate recipe + inventory, atomically remove inputs and add outputs via **`PlayerInventoryOperations`**, award **`refine`** XP via **`RefineActivitySkillXpGrant`** on success. **In scope** - `CraftOperations` / `CraftResult` types in `server/NeonSprawl.Server/Game/Crafting/`. - Stable deny reason codes: `unknown_recipe`, `insufficient_materials`, `inventory_full`, `invalid_quantity` (if quantity validated). - Rollback input removal when output grant fails. - Unit tests (AAA) with stub inventory + progression stores; NEO-42 grant invoked on success path. - Document **`CraftResult`** shape in module doc (server-internal until promoted to wire). **Out of scope** - Craft HTTP (E3M2-06). - Client HUD. - Skill level gates, station checks, craft failure RNG. **Acceptance criteria** - [ ] Successful craft removes configured inputs and adds outputs deterministically. - [ ] Insufficient materials returns stable `reasonCode` with **no** inventory change. - [ ] Full bag on output returns stable `reasonCode`; inputs are **not** consumed (or are rolled back). - [ ] **`refine`** skill XP increases by **10** on success (NEO-42). - [ ] **`make_prototype_armor`** respects equipment slot rules from E3.M3 when bag is full but equip slot empty. --- ### E3M2-06 — Craft HTTP + Bruno gather→refine→make spine **Goal:** Versioned **`POST /game/players/{id}/craft`** wired to craft engine; Bruno documents full prototype loop from gather through combat-relevant output. **In scope** - `POST /game/players/{id}/craft` + DTOs + API tests (AAA). - Known-player gate via `IPositionStateStore` (NEO-37 / NEO-55 pattern). - Bruno `bruno/neon-sprawl-server/craft/` with spine: seed or gather **`scrap_metal_bulk`** → **`refine_scrap_standard`** → **`make_field_stim_mk0`** → GET inventory + GET skill-progression shows **`refine`** XP delta. - Bruno deny paths: unknown recipe, insufficient materials, inventory full on output. - `server/README.md`; integration tests (AAA) where practical. **Out of scope** - Godot craft UI (optional follow-up). - Station interact / world bench placement. **Acceptance criteria** - [ ] POST success returns structured **`CraftResult`** (or HTTP envelope) matching engine outcome. - [ ] Bruno spine completes without manual inventory POST shortcuts (gather interact allowed for scrap). - [ ] At least one deny path documented per major `reasonCode`. - [ ] Epic 3 Slice 3 acceptance: player completes **gather → refine → usable item** (`field_stim_mk0` consumable). --- ### E3M2-07 — `item_crafted` / `craft_failed` telemetry hook sites **Goal:** Comment-only hooks on craft success and structured deny for future E9.M1 catalog events. **In scope** - Hook placement in **`CraftOperations`** (success + deny before commit — engine-only, mirror NEO-64 gather pattern). - `TODO(E9.M1)` comments; no production logging. - README + module doc pointer; note **`time-to-first-craft`** is a future metric derived from these events + session context (no separate hook in prototype). **Out of scope** - Telemetry ingest, dashboards. - HTTP-layer duplicate hooks if engine owns the contract. **Acceptance criteria** - [ ] Hook sites documented in code and `server/README.md`. - [ ] Matches Epic 3 Slice 3 telemetry vocabulary in [epic_03](../decomposition/epics/epic_03_crafting_economy.md#epic-3-slice-3). --- ## After this backlog - Epic 3 prototype **gather→craft** loop is complete for Phase 1 crafting relevance gate; Slice 4 (**E3.M4** + **E3.M5**) remains pre-production. - Track delivery in Linear; keep `blockedBy` links synchronized if scope changes. - For each issue kickoff, add `docs/plans/{NEO-XX}-implementation-plan.md` per [story-kickoff](../../.cursor/rules/story-kickoff.md). - Add `docs/manual-qa/{NEO-XX}.md` when craft HTTP lands (E3M2-06+). ## Related docs - [E3_M2_RefinementAndRecipeExecution.md](../decomposition/modules/E3_M2_RefinementAndRecipeExecution.md) - [E3M1-prototype-backlog.md](E3M1-prototype-backlog.md) - [E3M3-prototype-backlog.md](E3M3-prototype-backlog.md) - [epic_03_crafting_economy.md](../decomposition/epics/epic_03_crafting_economy.md) - [crafting.md](../game-design/crafting.md) - [NEO-42 implementation plan](NEO-42-implementation-plan.md)