# NEO-33 — Implementation plan

## Story reference

| Field | Value |
|--------|--------|
| **Key** | NEO-33 |
| **Title** | E2.M1: Lock prototype SkillDef trio + CI catalog contract |
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-33/e2m1-lock-prototype-skilldef-trio-ci-catalog-contract |

## Kickoff clarifications

| Topic | Question | Answer |
|--------|------------|--------|
| Prototype `SkillDef.id` freeze | Lock **salvage**, **refine**, **intrusion** (gather + process + tech)? | **Yes** — freeze these three ids as-is. |
| Designer note placement | Where should the short note on `allowedXpSourceKinds` + grant call sites live? | **Both** — brief pointer in `content/README.md`, fuller text in `docs/decomposition/modules/E2_M1_SkillDefinitionRegistry.md`. |
| `allowedXpSourceKinds` meaning | (User asked in chat.) | Explained in session: per-skill allowlist for XP grant channels (`activity`, `mission_reward`, `trainer`, `book_or_item`); E2.M2 rejects grants outside the set; combat encounter XP stays on gigs, not skills. |

## Goal, scope, and out-of-scope

**Goal:** Treat the repo as the source of truth for the **Slice 1 prototype skill catalog**: exactly **three** non-combat `SkillDef` rows covering **gather**, **process** (or make), and **tech**, with stable **`id`** values, validated on every PR against `content/schemas/skill-def.schema.json`, and documented for designers and downstream issues (NEO-34+).

**In scope (from Linear):**

- Align `content/skills/prototype_skills.json` (and schema only if contract gaps appear) with [`docs/game-design/skills.md`](../../game-design/skills.md) prototype trio policy (one Gather, one Process/Make, one Tech).
- Confirm [`.github/workflows/pr-gate.yml`](../../.github/workflows/pr-gate.yml) runs [`scripts/validate_content.py`](../../scripts/validate_content.py); close small gaps if any (e.g. clearer validation errors, doc pointers).
- Short designer note (**README + E2.M1** per kickoff): each row **must** declare non-empty `allowedXpSourceKinds` and what each kind implies for grant call sites.
- Call out the **frozen** `id` list in-repo for downstream references (E2.M2, E3, quests).

**Out of scope (from Linear):**

- Server runtime catalog load (NEO-34+).
- XP grant implementation (E2.M2 / NEO follow-ups).

## Acceptance criteria checklist

- [x] PR / CI fails on invalid catalog rows, schema mismatch, or **duplicate** skill `id` across `content/skills/*.json`.
- [x] Exactly **three** prototype skills with category coverage **gather** + **process** (or make) + **tech**; **`id`** values **salvage**, **refine**, **intrusion** recorded as **stable / frozen** in decomposition or authoring docs.
- [x] CT.M1 and E2.M1 doc pointers remain accurate relative to what ships in this story.
- [x] Designer-facing note on `allowedXpSourceKinds` appears in **`content/README.md`** (brief) and **`E2_M1_SkillDefinitionRegistry.md`** (detail).

## Technical approach

1. **Catalog:** Keep `content/skills/prototype_skills.json` as the single skills JSON under `content/skills/` for the prototype trio (already matches gather / process / tech). Adjust rows or schema only if validation or design review finds drift from `skills.md` policy.
2. **CI:** Rely on existing `validate_content.py` + PR gate; optionally tighten messages or add explicit checks (e.g. total distinct `id` count equals three for Slice 1) if product wants fail-fast on accidental extra files—only if agreed during implementation without scope creep.
3. **Docs:** Update **E2.M1** with a **Prototype Slice 1 freeze** subsection: frozen ids, category coverage, and a **grant channels** subsection expanding `allowedXpSourceKinds` for designers (link to schema enum). Update **content/README.md** with a short pointer to that section and a one-line “do not rename ids” reminder.
4. **Cross-doc:** Update `skills.md` “Next pass” / prototype language so it points at the **locked** ids (no longer an open pick). Refresh [`documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) E2.M1 row if it still implies NEO-33 work is only backlog.
5. **No application server or C# changes** in this story unless an unexpected coupling appears (unlikely).

## Files to add

| Path | Purpose |
|------|---------|
| **None** | All contracts and catalogs already exist; this story is documentation alignment, optional validation tightening, and explicit freeze callouts—not new modules. |

## Files to modify

| Path | Rationale |
|------|------------|
| `docs/decomposition/modules/E2_M1_SkillDefinitionRegistry.md` | Add frozen trio + designer-oriented `allowedXpSourceKinds` / grant call-site guidance (canonical). |
| `content/README.md` | Brief pointer to E2.M1 detail and freeze reminder for content authors. |
| `docs/game-design/skills.md` | Point “prototype trio” at locked ids so design doc matches data. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | Update E2.M1 / NEO-33 row to match shipped state after implementation. |
| `scripts/validate_content.py` | Shipped **NEO-33** Slice 1 gate: exact frozen ids + category coverage after schema + duplicate-id checks. |

## Tests

| Item | Coverage |
|------|----------|
| **Automated** | Existing **PR gate** job runs `python scripts/validate_content.py` — primary regression signal for schema + duplicate `id`. No dedicated pytest module exists in-repo today; introducing one is **optional** and not required for NEO-33 acceptance if CI + local script remain the contract. |
| **Manual** | Run `pip install -r scripts/requirements-content.txt && python scripts/validate_content.py` locally after doc/catalog edits; skim PR gate workflow in GitHub on the story PR. |

## Open questions / risks

- **Catalog growth:** `validate_content.py` now requires **exactly** the three frozen ids; expanding the roster requires a follow-up issue to relax or replace this gate.
- **None** otherwise at kickoff.

## Decisions

| Decision | Rationale |
|----------|------------|
| Frozen ids **salvage**, **refine**, **intrusion** | User confirmation during kickoff. |
| Designer note in **README + E2.M1** | User picked option **3**. |

## Implementation notes (shipped)

- **`scripts/validate_content.py`:** after schema validation, enforces `PROTOTYPE_SLICE1_SKILL_IDS` and category coverage (gather, tech, process or make).
- **Docs:** E2.M1 designer note + freeze table; `content/README` pointer; `skills.md` frozen trio bullet; CT.M1 CI paragraph; alignment + dependency register **E2.M1** → **In Progress**.
