NEO-50 — Implementation plan
Story reference
Kickoff clarifications
| Topic |
Question |
Answer |
| Prototype role field |
prototypeRole vs designBucket vs tags only? |
Agent recommendation (accepted): prototypeRole enum — one value per frozen archetype (material | intermediate | consumable | utility | quest_token | equip_stub). CI requires exactly one row per role (same pattern as skill category coverage). User asked for suggestion; design buckets in items.md overlap and are not 1:1 with the six-id spine. |
| Stack + slot metadata |
Explicit fields vs deferred? |
Agent recommendation (accepted): Explicit per row: stackMax (int ≥ 1) and inventorySlotKind (bag | equipment). Locks stack/slot rules in content before NEO-51 server load and E3M3-05 inventory engine. |
| Forward-compat stubs |
Optional rarity / bindPolicy / durabilityMax on schema? |
Yes — optional fields on ItemDef; omit on all six prototype rows (documented unused in v1). User confirmed. |
| Catalog envelope |
File shape? |
Mirror skills: { "schemaVersion": 1, "items": [ … ] } in content/items/prototype_items.json; each row validates against content/schemas/item-def.schema.json. |
| Base branch |
Branch from main after PR #84? |
Yes — main includes E3M3-prototype-backlog and README freeze pointers. |
Frozen prototype ids (Slice 1 — item spine)
id |
prototypeRole |
inventorySlotKind |
stackMax (initial) |
scrap_metal_bulk |
material |
bag |
999 |
refined_plate_stock |
intermediate |
bag |
999 |
field_stim_mk0 |
consumable |
bag |
20 |
survey_drone_kit |
utility |
bag |
1 |
contract_handoff_token |
quest_token |
bag |
1 |
prototype_armor_shell |
equip_stub |
equipment |
1 |
Rules: Do not rename id values after ship — change displayName only, or add a new id in a follow-up issue. Relaxing the six-id or six-role CI gate requires a new Linear issue.
Goal, scope, and out-of-scope
Goal: Lock the six-item prototype catalog under content/items/ with JSON Schema and CI validation (scripts/validate_content.py) so ids, stack rules, and slot metadata are stable before server load (NEO-51+).
In scope (from Linear / E3M3-prototype-backlog):
content/schemas/item-def.schema.json — single-row ItemDef contract.
content/items/prototype_items.json — six frozen ids (table above).
scripts/validate_content.py — schema validation, duplicate id, prototypeRole coverage, exact six-id allowlist.
- Designer note in E3.M3 module doc +
content/README.md: stack rules, bag vs equipment slot, v1 scope (no durability mutation).
Out of scope (from Linear):
- Server runtime load, inventory store, HTTP, client HUD (NEO-51+).
Acceptance criteria checklist
Technical approach
- Row schema (
item-def.schema.json): Draft 2020-12 object; additionalProperties: false. Required: id, displayName, prototypeRole, stackMax, inventorySlotKind. Enums: prototypeRole (six values above), inventorySlotKind (bag | equipment). id pattern ^[a-z][a-z0-9_]*$ (same as skills). Optional stubs: rarity (common | uncommon | rare | epic), bindPolicy (none | bind_on_acquire | bind_on_equip), durabilityMax (integer ≥ 0) — all optional, unused in prototype rows.
- Catalog file:
content/items/prototype_items.json with schemaVersion: 1 and six rows matching the freeze table; placeholder displayName strings acceptable.
validate_content.py: After existing skill / level-curve / mastery validation (or in parallel before final success):
- Discover
content/items/*_items.json.
- Validate each row against
item-def.schema.json.
- Track
seen_ids across files; reject duplicates.
_prototype_slice1_item_gate: ids must equal PROTOTYPE_SLICE1_ITEM_IDS; roles must cover each prototypeRole exactly once; equip_stub row must use inventorySlotKind: equipment and stackMax: 1 (post-schema sanity).
- Docs: E3.M3 — Prototype Slice 1 freeze subsection + designer note (bag vs equipment, stackMax, no durability mutation in v1).
content/README.md — promote items path from “planned” to active with freeze list. CT.M1 — add item catalog paragraph. documentation_and_implementation_alignment.md — E3.M3 row → In Progress / NEO-50.
- No server/C#/client changes in this story.
Files to add
| Path |
Purpose |
content/schemas/item-def.schema.json |
JSON Schema for a single ItemDef row. |
content/items/prototype_items.json |
Prototype six-item catalog (schemaVersion + items array). |
Files to modify
| Path |
Rationale |
scripts/validate_content.py |
Load/validate item catalogs; duplicate id; Slice 1 six-id + six-role gates; update success summary line. |
content/README.md |
Document content/items/, schema path, Slice 1 freeze (active, not “planned”). |
docs/decomposition/modules/E3_M3_ItemizationAndInventorySchema.md |
Freeze table, designer note (stack/slot/v1 scope), link to plan. |
docs/decomposition/modules/CT_M1_ContentValidationPipeline.md |
PR gate paragraph for item catalog validation. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
E3.M3 / NEO-50 status after catalog lands. |
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 id in catalog → non-zero exit; (2) remove one frozen id → gate error; (3) break schema (invalid stackMax) → schema error; (4) swap two prototypeRole values → role coverage error. |
| Manual happy path |
pip install -r scripts/requirements-content.txt && python3 scripts/validate_content.py — reports item catalog + existing catalogs OK. |
No dedicated pytest module in-repo today (same as NEO-33 / NEO-45).
Open questions / risks
- Catalog growth: Slice 1 gate requires exactly the six frozen ids; expanding the roster needs a follow-up issue to change
PROTOTYPE_SLICE1_ITEM_IDS and role gate.
- C# vs Python drift: None until NEO-51; document “keep in sync” constant names in both places when server load lands.
- None otherwise at kickoff.
Decisions
| Decision |
Rationale |
prototypeRole enum + per-role CI |
User requested agent recommendation; maps 1:1 to six-id vertical slice. |
Explicit stackMax + inventorySlotKind |
User requested agent recommendation; backlog requires stack/slot metadata before server load. |
Optional rarity / bindPolicy / durabilityMax on schema |
User chose yes_stubs for forward-compat without populating prototype rows. |