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

9.9 KiB
Raw Blame History

NEO-65 — Implementation plan

Story reference

Field Value
Key NEO-65
Title E3.M2: Prototype RecipeDef catalog + schemas + CI
Linear https://linear.app/neon-sprawl/issue/NEO-65/e3m2-prototype-recipedef-catalog-schemas-ci
Module E3.M2 — RefinementAndRecipeExecution · Epic 3 Slice 3 · backlog E3M2-01
Branch NEO-65-e3m2-prototype-recipedef-catalog-schemas-ci
Upstream NEO-50 — frozen six-item catalog; prototype skill trio includes refine.
Pattern NEO-50 / NEO-57 — row JSON Schema + catalog envelope + scripts/validate_content.py Slice gate
Blocks NEO-66 — server recipe catalog load

Kickoff clarifications

No blocking clarifications needed. Linear AC, E3M2-prototype-backlog — E3M2-01, E3.M2 kickoff decisions, and the Slice 3 freeze table fully specify recipe count, ids, I/O quantities, recipeKind, and requiredSkillId. Schema shape follows NEO-50 / NEO-57 precedent:

Topic Agent recommendation Status
Catalog envelope { "schemaVersion": 1, "recipes": [ … ] } in content/recipes/prototype_recipes.json (mirror items / resource-nodes). Adopted (NEO-50 precedent)
displayName on RecipeDef Required — mirror ItemDef; supports E3M2-04 GET without schema churn. Adopted (NEO-50 / NEO-57 precedent)
Optional forward-compat stubs Optional requiredSkillLevel (int ≥ 0) and stationTag (string) on recipe-def.schema.json; omitted on all eight prototype rows (Slice 3 has no gates / stations). Adopted (NEO-50 stub pattern)
requiredSkillLevel in prototype rows Omitted — decomposition default; no level gates in Slice 3. Adopted (backlog kickoff table)

Goal, scope, and out-of-scope

Goal: Lock content shape and CI validation for eight frozen RecipeDef rows before server load (NEO-66+).

In scope (from Linear + E3M2-01):

  • content/schemas/recipe-def.schema.json and content/schemas/recipe-io-row.schema.json.
  • content/recipes/prototype_recipes.json — eight frozen ids (freeze table below).
  • scripts/validate_content.py — schema validation, duplicate id, exact eight-recipe allowlist, recipeKind coverage (process + make), cross-check every input/output itemId against item catalogs, every requiredSkillId against skill catalogs (prototype gate: all refine).
  • Designer note in E3.M2 module doc + content/README.md Slice 3 paragraph.
  • CT.M1 PR gate paragraph for recipe catalogs.

Out of scope (from Linear):

  • Server loader, craft engine, HTTP (NEO-66+).

Acceptance criteria checklist

  • 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.

Technical approach

  1. Row schema — I/O row (recipe-io-row.schema.json): Draft 2020-12 object; additionalProperties: false. Required: itemId, quantity. itemId pattern ^[a-z][a-z0-9_]*$. quantity integer ≥ 1.

  2. Row schema — RecipeDef (recipe-def.schema.json): Draft 2020-12 object; additionalProperties: false. Required: id, displayName, recipeKind, requiredSkillId, inputs, outputs. id pattern ^[a-z][a-z0-9_]*$. recipeKind enum: process | make. requiredSkillId string (same id pattern). inputs / outputs: arrays with minItems: 1; each element validates against recipe-io-row.schema.json (via $ref). Optional stubs (unused in prototype rows): requiredSkillLevel (int ≥ 0), stationTag (string).

  3. Catalog file: content/recipes/prototype_recipes.json with schemaVersion: 1 and eight rows from the freeze table; placeholder displayName strings acceptable.

  4. Frozen content values (prototype Slice 3):

    RecipeDef.id recipeKind inputs → outputs
    refine_scrap_standard process 5× scrap_metal_bulk → 1× refined_plate_stock
    refine_scrap_efficient process 10× scrap_metal_bulk → 2× refined_plate_stock
    make_field_stim_mk0 make 2× refined_plate_stock + 1× scrap_metal_bulk → 1× field_stim_mk0
    make_field_stim_batch make 5× refined_plate_stock + 3× scrap_metal_bulk → 3× field_stim_mk0
    make_prototype_armor make 8× refined_plate_stock + 5× scrap_metal_bulk → 1× prototype_armor_shell
    make_armor_quick make 4× refined_plate_stock + 3× scrap_metal_bulk → 1× prototype_armor_shell
    make_survey_drone_kit make 3× refined_plate_stock + 2× scrap_metal_bulk → 1× survey_drone_kit
    make_contract_token make 2× refined_plate_stock + 1× scrap_metal_bulk → 1× contract_handoff_token

    All rows: requiredSkillId: "refine". No requiredSkillLevel or stationTag on prototype rows.

  5. validate_content.py:

    • Add RECIPE_SCHEMA, RECIPE_IO_ROW_SCHEMA, RECIPES_DIR; discover content/recipes/*_recipes.json.
    • _validate_recipe_catalogs: envelope schemaVersion: 1, top-level recipes array; validate each row against recipe-def.schema.json; track seen_ids; reject duplicates across files.
    • Post-schema cross-checks per row: every I/O itemId ∈ loaded item ids; requiredSkillId ∈ loaded skill ids.
    • _prototype_slice3_recipe_gate: ids must equal PROTOTYPE_SLICE3_RECIPE_IDS; recipeKind set must include process and make; every row requiredSkillId must be refine (prototype Slice 3 contract).
    • Run recipe validation after skill + item catalogs succeed (reuse seen_ids / item_seen_ids).
    • Require recipe schema files in main() startup checks; extend success summary with recipe file + id counts.
    • Keep PROTOTYPE_SLICE3_RECIPE_IDS in sync with E3.M2 freeze table and future NEO-66 server loader.
  6. Docs:

    • content/README.md — ensure recipes/ table row + Slice 3 freeze paragraph (may already exist on branch from decomposition prep).
    • E3_M2_RefinementAndRecipeExecution.md — ensure freeze table + CI rules line (may already exist on branch).
    • CT_M1_ContentValidationPipeline.md — recipe catalog PR gate paragraph.
    • documentation_and_implementation_alignment.md — E3.M2 row → In Progress / NEO-65.
    • E3M2-prototype-backlog.md — E3M2-01 checkboxes when implementation completes.
  7. No server/C#/client changes in this story.

Files to add

Path Purpose
content/schemas/recipe-io-row.schema.json JSON Schema for a single recipe input/output row.
content/schemas/recipe-def.schema.json JSON Schema for a single RecipeDef row.
content/recipes/prototype_recipes.json Prototype eight-recipe catalog (schemaVersion + recipes array).
docs/plans/NEO-65-implementation-plan.md This plan.

Files to modify

Path Rationale
scripts/validate_content.py Load/validate recipe catalogs; duplicate id; Slice 3 eight-id + recipeKind + refine gates; I/O item + skill cross-checks; success summary.
content/README.md Document content/recipes/, schema paths, Slice 3 freeze (active catalog).
docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md Confirm freeze table + CI enforcement note; link to plan when complete.
docs/decomposition/modules/CT_M1_ContentValidationPipeline.md PR gate paragraph for recipe catalog validation.
docs/decomposition/modules/documentation_and_implementation_alignment.md E3.M2 / NEO-65 status after catalog lands.
docs/plans/E3M2-prototype-backlog.md E3M2-01 checkboxes + landed note when complete.

Tests

Item Coverage
CI / PR gate .github/workflows/pr-gate.yml already runs python scripts/validate_content.py — extended script is the regression signal.
Manual negative cases From repo root after implementation: (1) duplicate recipe id → non-zero exit; (2) remove one frozen id → gate error; (3) unknown itemId in inputs → cross-check error; (4) requiredSkillId: "salvage" → Slice 3 gate error; (5) break schema (empty outputs) → schema error.
Manual happy path pip install -r scripts/requirements-content.txt && python3 scripts/validate_content.py — reports recipe catalog + existing catalogs OK.

No dedicated pytest module in-repo today (same as NEO-50 / NEO-57).

Open questions / risks

Question / risk Agent recommendation Status
Catalog growth Slice 3 gate requires exactly the eight frozen ids; expanding the roster needs a follow-up issue to change PROTOTYPE_SLICE3_RECIPE_IDS. adopted
C# vs Python drift None until NEO-66; document “keep in sync” constant names in both places when server load lands. adopted
Branch carries E3M2 decomposition prep Commit decomposition doc deltas (freeze table, backlog, README) with implementation or as first doc batch on this branch. done — prep landed in prior commits; catalog + CI in this story.

None blocking.