15 KiB
E3.M2 — Prototype story backlog (RefinementAndRecipeExecution)
Working backlog for Epic 3 — Slice 3 (recipes and crafting pipeline). Decomposition and contracts: E3.M2 — RefinementAndRecipeExecution.
Labels (Linear): every issue E3.M2; add server / Story as listed per issue.
Precursor (do not re-scope): 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 Slice 1 — frozen item catalog, IPlayerInventoryStore, PlayerInventoryOperations, inventory HTTP (NEO-50–NEO-56). E3.M1 Slice 2 — gather → scrap_metal_bulk (NEO-57–NEO-64). E2.M2 grant path (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 |
| E3M2-02 | NEO-66 |
| E3M2-03 | NEO-67 |
| E3M2-04 | NEO-68 |
| E3M2-05 | NEO-69 |
| E3M2-06 | NEO-70 |
| E3M2-07 | 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 quest steps requiring crafted items; E3.M4 durability sinks; E4.M1 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 |
| 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 |
| Craft wire surface | POST /game/players/{id}/craft |
Mirror 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) |
| 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.jsonandcontent/schemas/recipe-io-row.schema.json(input/output row shape).content/recipes/prototype_recipes.jsonwith stableidvalues (see module doc freeze table).scripts/validate_content.py: schema validation, duplicate ids, exact eight-recipe allowlist,recipeKindcoverage (process+make), cross-check that everyitemIdin inputs/outputs exists incontent/items/, everyrequiredSkillIdexists incontent/skills/.- Designer note in E3_M2 +
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
idfails CI. - Every input/output references a valid frozen item id; every recipe references
refine. - Stable id list documented in module doc freeze box.
Landed (NEO-65): content/recipes/prototype_recipes.json, recipe-def.schema.json, recipe-io-row.schema.json, CI gates in validate_content.py; plan NEO-65-implementation-plan.md.
E3M2-02 — Server recipe catalog load (fail-fast)
Goal: Disk → host: startup load of content/recipes/*.json with CI-parity validation.
Kickoff note (NEO-66): Duplicate PROTOTYPE_SLICE3_RECIPE_IDS (and Slice 3 gate rules) in C# with an explicit “keep in sync with scripts/validate_content.py” comment — mirror NEO-51 PrototypeSlice1ItemCatalogRules / PROTOTYPE_SLICE1_ITEM_IDS.
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-checkISkillDefinitionRegistry). - 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
RecipeDefand I/O rows by recipeid.
Landed (NEO-66): server/NeonSprawl.Server/Game/Crafting/ — RecipeDefinitionCatalogLoader, RecipeDefinitionCatalog, PrototypeSlice3RecipeCatalogRules; plan NEO-66-implementation-plan.md.
E3M2-03 — Recipe definition registry + DI
Goal: Injectable IRecipeDefinitionRegistry consumed by craft engine and HTTP.
In scope
RecipeDefinitionRegistryimplementation + 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
recipeIddistinguishable from valid ids in tests.
Landed (NEO-67): IRecipeDefinitionRegistry + RecipeDefinitionRegistry in server/NeonSprawl.Server/Game/Crafting/; plan NEO-67-implementation-plan.md.
E3M2-04 — GET world recipe-definitions
Goal: Versioned read-only catalog for Bruno, client preview, and tooling — mirror 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.mdsection.
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.
Landed (NEO-68): GET /game/world/recipe-definitions — RecipeDefinitionsWorldApi + DTOs in server/NeonSprawl.Server/Game/Crafting/; plan NEO-68-implementation-plan.md; manual QA NEO-68; Bruno bruno/neon-sprawl-server/recipe-definitions/.
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/CraftResulttypes inserver/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
CraftResultshape 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
reasonCodewith no inventory change. - Full bag on output returns stable
reasonCode; inputs are not consumed (or are rolled back). refineskill XP increases by 10 on success (NEO-42).make_prototype_armorrespects equipment slot rules from E3.M3 when bag is full but equip slot empty.
Landed (NEO-69): CraftOperations.TryCraft + CraftResult in server/NeonSprawl.Server/Game/Crafting/; plan NEO-69-implementation-plan.md; server README — Craft engine (NEO-69).
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 gatherscrap_metal_bulk→refine_scrap_standard→make_field_stim_mk0→ GET inventory + GET skill-progression showsrefineXP 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 — Epic 3 Slice 5 (NEO-74).
- 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_mk0consumable).
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-craftis 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.
After this backlog
- Epic 3 prototype server gather/craft spine complete when E3M2-07 lands; playable client loop completes with Epic 3 Slice 5 (NEO-72–NEO-75).
- Slice 4 (E3.M4 + E3.M5) sinks/policy remains pre-production.
- Track delivery in Linear; keep
blockedBylinks synchronized if scope changes. - For each issue kickoff, add
docs/plans/{NEO-XX}-implementation-plan.mdper story-kickoff. - Add
docs/manual-qa/{NEO-XX}.mdwhen craft HTTP lands (E3M2-06+).