neon-sprawl/docs/decomposition/modules/E2_M1_SkillDefinitionRegist...

116 lines
7.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# E2.M1 — SkillDefinitionRegistry
## Summary
| Field | Value |
|--------|--------|
| **Module ID** | E2.M1 |
| **Epic** | [Epic 2 — Skills and Progression Framework](../epics/epic_02_skills_and_progression.md) |
| **Linear (Epic 2 project)** | [Epic 2 — Classless Skill and Progression Framework](https://linear.app/neon-sprawl/project/epic-2-classless-skill-and-progression-framework-5200f84bf8b6) |
| **Stage target** | Prototype |
| **Status** | In Progress (see [dependency register](module_dependency_register.md)) |
## Linear backlog (Epic 2 Slice 1)
Issues use label **`E2.M1`**. Dependency order: **NEO-33 → NEO-34 → NEO-35 → NEO-36** (each issues description is canonical).
| Order | Issue | Summary |
|------:|-------|---------|
| 1 | [NEO-33](https://linear.app/neon-sprawl/issue/NEO-33/e2m1-lock-prototype-skilldef-trio-ci-catalog-contract) | Lock prototype `SkillDef` trio + CI catalog contract + designer note |
| 2 | [NEO-34](https://linear.app/neon-sprawl/issue/NEO-34/e2m1-server-loads-skilldef-catalog-at-startup-fail-fast) | Server loads catalog at startup (fail-fast) |
| 3 | [NEO-35](https://linear.app/neon-sprawl/issue/NEO-35/e2m1-skilldefinitionregistry-service-lookup-tests) | `SkillDefinitionRegistry` service + lookup tests |
| 4 | [NEO-36](https://linear.app/neon-sprawl/issue/NEO-36/e2m1-read-only-skill-catalog-http-projection-bruno) | Read-only skill catalog HTTP projection + Bruno |
`docs/plans/NEO-*-implementation-plan.md` files should be added when implementation kicks off (per [planning-implementation-docs](../../../.cursor/rules/planning-implementation-docs.md)).
## Purpose
Central catalog for **non-combat** skill metadata: **stable IDs**, **categories**, **allowed XP sources**, and unlock prerequisites. All prototype activities reference skills by **`SkillDef.id`** validated against this registry. **Combat role progression** is **not** a `SkillDef`—use gig progression ([`docs/game-design/gigs.md`](../../game-design/gigs.md)).
## Data contract (schema)
| Artifact | Path |
|----------|------|
| **JSON Schema** | [`content/schemas/skill-def.schema.json`](../../../content/schemas/skill-def.schema.json) |
| **Sample catalog** | [`content/skills/prototype_skills.json`](../../../content/skills/prototype_skills.json) |
**`SkillDef` fields (prototype):**
| Field | Rule |
|--------|------|
| **`id`** | Lowercase `snake_case`, **immutable** after ship. Used in saves, recipes, quests, `XpGrantEvent.skillId`, telemetry. **Never** rename to “fix” a skill—add a new `id` and migrate content if a skill splits. |
| **`category`** | One of `gather` \| `process` \| `make` \| `tech` — aligned with [`docs/game-design/skills.md`](../../game-design/skills.md) roster buckets (not gigs). |
| **`displayName`** | Player-facing string; **may change** without migrating progression data. |
| **`allowedXpSourceKinds`** | Non-empty set of grant channels [E2.M2](E2_M2_XpAwardAndLevelEngine.md) may apply to this skill. **Combat encounters** must **not** use skill XP—those award **gig XP**; `mission_reward` covers explicit mission/contract skill payouts. |
**`allowedXpSourceKinds` values:**
| Kind | Meaning |
|------|--------|
| `activity` | Primary loop (e.g. gather node complete, recipe craft complete, skill-based world interaction)—callers set `XpGrantEvent.sourceKind` to match. |
| `mission_reward` | Quest/contract hand-in or other scripted payout ([`docs/game-design/progression.md`](../../game-design/progression.md) **Mission rewards vs combat XP**). |
| `trainer` | NPC or teaching beat. |
| `book_or_item` | Book, chip, training consumable, or similar one-shot grant. |
Add new enum values only with a **schema version** / migration note in [CT.M1](CT_M1_ContentValidationPipeline.md) when CI enforces catalogs.
### Designer note: allowedXpSourceKinds and grant call sites
Every `SkillDef` row **must** include a **non-empty** `allowedXpSourceKinds` array (enforced by JSON Schema). Designers use it to agree with engineering **which systems may ever grant XP** to that skill:
- **`activity`** — wire gather nodes, recipe completions, skill-based world objectives, and similar **primary-loop** payouts; set `XpGrantEvent.sourceKind` (or successor) to `activity` on those code paths.
- **`mission_reward`** — wire **explicit** mission/contract hand-ins or scripted bonuses only; not a stand-in for combat encounter XP ([`docs/game-design/progression.md`](../../game-design/progression.md) — mission vs combat).
- **`trainer`** / **`book_or_item`** — wire NPC teaching beats and consumable/one-shot grants respectively; do not use for combat kills.
**Combat encounters award gig XP, not skill XP.** If a grant channel is missing from a skills list, [E2.M2](E2_M2_XpAwardAndLevelEngine.md) rejects the grant at apply time (**NEO-38** HTTP grant path)—keep catalogs honest so call sites do not drift.
## Prototype Slice 1 freeze ([NEO-33](https://linear.app/neon-sprawl/issue/NEO-33))
The **first shipped trio** under `content/skills/*.json` is **frozen** for downstream references (quests, recipes, E2.M2, E3) until a deliberate migration or follow-up issue expands the roster.
| `SkillDef.id` | `category` | Role |
|---------------|------------|------|
| **`salvage`** | `gather` | Prototype Gather anchor |
| **`refine`** | `process` | Prototype Process anchor (satisfies “Process or Make” for Slice 1) |
| **`intrusion`** | `tech` | Prototype Tech anchor |
**Rules:** Do **not** rename these `id` values to “fix” wording—change `displayName` only, or add a new `id` and migrate content. CI ([`scripts/validate_content.py`](../../../scripts/validate_content.py)) enforces **exactly** these three ids across all skill JSON files and **category coverage** (gather + tech + at least one of process/make). Relaxing or changing that gate belongs in a new Linear issue once the catalog intentionally grows.
## Responsibilities
- Load and validate `SkillDef` from data; expose `SkillCategory` and `UnlockRequirement` for gating.
- Reject unknown `skillId` or **XP grants** whose `sourceKind` is **not** listed on the target skills `allowedXpSourceKinds` (enforced in [E2.M2](E2_M2_XpAwardAndLevelEngine.md) at grant time).
## Key contracts
| Contract | Role |
|----------|------|
| `SkillDef` | Per-skill metadata: **id**, **category**, **displayName**, **allowedXpSourceKinds** (+ future unlock fields). |
| `SkillCategory` | `gather` \| `process` \| `make` \| `tech` for UX and balance. |
| `UnlockRequirement` | Prerequisites before XP or use counts apply. |
## Acceptance criteria (E2.M1 / Slice 1)
- Every loaded `SkillDef` validates against [`skill-def.schema.json`](../../../content/schemas/skill-def.schema.json) in CI ([CT.M1](CT_M1_ContentValidationPipeline.md#ci-prototype) — [`scripts/validate_content.py`](../../../scripts/validate_content.py) in **PR gate**) or at server boot.
- **Duplicate `id`** in a catalog fails validation.
- **Roster freeze for prototype:** Slice 1 locks **`salvage`**, **`refine`**, and **`intrusion`**; their **`id`** values are **stable**—downstream content (recipes, quests) may reference them; changing an `id` requires a **migration** or new row.
- Document for designers: each new skill row **must** declare **`allowedXpSourceKinds`** so engineers know which systems may grant XP (gather/craft hooks vs E7.M2 mission rewards only, etc.).
## Module dependencies
- **None**
## Dependents (by design)
- **E2.M2** — XpAwardAndLevelEngine (primary): validates grants against `allowedXpSourceKinds`.
## Related implementation slices
Epic 2 **Slice 1** — skill registry and prototype **non-combat** skills; invalid references fail at load or in CI.
## Source anchors
- Game design: [`docs/game-design/skills.md`](../../game-design/skills.md) — roster, **XP philosophy**, gig/skill boundary.
- Master plan: [`neon_sprawl_vision.plan.md`](../../../neon_sprawl_vision.plan.md) — Epic 2.
- [Module dependency register](module_dependency_register.md)