diff --git a/docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md b/docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md index df40124..c4ddeb5 100644 --- a/docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md +++ b/docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md @@ -45,7 +45,9 @@ Mastery tracks and perk unlock state so **skill** builds have expressive identit | **JSON Schema** | [`content/schemas/mastery-catalog.schema.json`](../../../content/schemas/mastery-catalog.schema.json) | | **Prototype catalog** | [`content/mastery/prototype_salvage_mastery.json`](../../../content/mastery/prototype_salvage_mastery.json) | -Each catalog file has top-level **`schemaVersion`**, a **`perks`** map (`perkId` → **`PerkDef`**), and a **`tracks`** array. Branches reference perks by id string only (no inline perk objects). +Each catalog file has top-level **`schemaVersion`**, a **`perks`** map (`perkId` → **`PerkDef`**), and a **`tracks`** array. Branches reference perks by id string only (no inline perk objects). CI requires each **`perks`** map key to equal the nested **`PerkDef.id`** (see [`mastery-catalog.schema.json`](../../../content/schemas/mastery-catalog.schema.json) and [`validate_content.py`](../../../scripts/validate_content.py)). + +**`tracks[]` → `MasteryTrack` → `tiers[]`:** each tier row has **`tierIndex`** (1-based, stable for `PerkState`), **`requiredLevel`** (skill level from E2.M2), and **`branches[]`**. Each branch has **`branchId`**, optional **`displayName`**, and **`perkIds[]`** (may be empty at tier 1). **`MasteryTrack` fields (prototype):** @@ -69,6 +71,11 @@ Each catalog file has top-level **`schemaVersion`**, a **`perks`** map (`perkId` - **One branch per tier:** picking `scrap_efficiency` forecloses `bulk_haul` for that tier; perks on the chosen branch unlock; the other branch’s perks for that tier do **not** unlock. - **Other skills** (`refine`, `intrusion`) have **no** mastery rows in the prototype catalog until a deliberate expansion issue relaxes CI. - **Respec** is not implemented in v1 — document in content if a perk is “permanent for the character.” +- **`displayName`** on a branch row is optional in schema; tier 1 and tier 2 may both include it for consistent authoring. + +## NEO-46 handoff (server catalog load) + +When [NEO-46](https://linear.app/neon-sprawl/issue/NEO-46) kicks off, mirror the Python CI gates documented in [NEO-45 implementation plan](../../plans/NEO-45-implementation-plan.md) (`PROTOTYPE_SLICE4_SALVAGE_SKILL_ID`, exactly one track, unknown `skillId`, duplicate `perkId`, branch-set equality tier-to-tier, unreferenced `perks` entries). Consider **`tierIndex`** uniqueness and sequential 1..N per track at server boot (schema allows gaps today). ## Prototype Slice 4 freeze — salvage flagship ([NEO-45](https://linear.app/neon-sprawl/issue/NEO-45)) @@ -111,6 +118,8 @@ The **first shipped mastery track** is **`salvage`** only: ## Acceptance criteria (E2.M3 / Slice 4) +Module-level bullets below span **NEO-45 → NEO-49**. **[NEO-45](https://linear.app/neon-sprawl/issue/NEO-45)** satisfies **data + CI** only (catalog, schema, `validate_content.py`); runtime unlock, HTTP, and telemetry are **NEO-46+**. + - At least one **salvage** mastery path unlocks a perk **without** blocking progression in **`refine`** or **`intrusion`** (separate skills / tracks). - Branch pick at tier 1 is **mutually exclusive**; server denies invalid or duplicate picks with stable **`reasonCode`** values. - Catalog + perk state validated in CI or at server boot ([CT.M1](CT_M1_ContentValidationPipeline.md)). diff --git a/docs/game-design/gigs.md b/docs/game-design/gigs.md index 8e716e9..7f2cf1e 100644 --- a/docs/game-design/gigs.md +++ b/docs/game-design/gigs.md @@ -63,7 +63,7 @@ Names below are **v1** after a theme pass; abilities and lore can still rename a **Open:** - **Curves:** same philosophy as **skills** (soft feel, more XP per level)—per-gig tables vs global template **TBD**. -- **Mastery/perks** on gigs vs flat level gates—**open** (E2.M3-style expression). +- **Mastery/perks** on gigs vs flat level gates—**open** (branch/perk depth **analogous** to [E2.M3 skill mastery](../decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md), **not** the E2.M3 module itself—gig trees stay a **separate** system per [skills.md](skills.md)). ## Hub swap diff --git a/docs/plans/E2M3-pre-production-backlog.md b/docs/plans/E2M3-pre-production-backlog.md index e1d983b..b4dd546 100644 --- a/docs/plans/E2M3-pre-production-backlog.md +++ b/docs/plans/E2M3-pre-production-backlog.md @@ -61,6 +61,8 @@ Working backlog for **Epic 2 — Slice 4** ([mastery and pacing](../decompositio **Goal:** Disk → host: startup load of mastery catalog(s) with cross-check against `ISkillDefinitionRegistry`. +**NEO-46 kickoff:** mirror CI gate constants from [NEO-45 implementation plan](NEO-45-implementation-plan.md) (see E2.M3 **NEO-46 handoff**). + **In scope** - Loader + `IMasteryCatalogRegistry` under `server/NeonSprawl.Server/Game/Skills/` (or `Game/Mastery/` if split). diff --git a/docs/reviews/2026-05-16-NEO-45.md b/docs/reviews/2026-05-16-NEO-45.md index e51fe2d..ad5315b 100644 --- a/docs/reviews/2026-05-16-NEO-45.md +++ b/docs/reviews/2026-05-16-NEO-45.md @@ -8,30 +8,30 @@ ## Verdict -**Approve with suggestions** +**Approve** ## Summary -NEO-45 documentation is largely coherent with shipped content: the implementation plan, E2.M3 module freeze table, CT.M1 CI paragraph, `content/README.md`, and alignment row accurately describe the **salvage-only** prototype track, tier gates (2 / 4), branch ids, perk ids, and CI gates. Hybrid vocabulary is preserved (skill mastery vs gig progression; no separate mastery XP bar; skill level from E2.M2). The main gaps are **tracking drift**: E2.M3 module and dependency register still say **Planned** while NEO-45 content is landed; the pre-production backlog leaves E2M3-01 acceptance boxes unchecked though the implementation plan marks them complete. The E2.M3 **Data contract** section should name the top-level **`perks` map** explicitly (decided at kickoff, present in schema and plan, omitted from the module doc body). +NEO-45 documentation is coherent with shipped content: implementation plan, E2.M3 module (freeze table, **`perks` map** data contract, **In Progress** status, NEO-46 handoff), CT.M1, `content/README.md`, alignment/register rows, and E2M3-01 backlog checkboxes align with the **salvage-only** prototype track and CI gates. Hybrid vocabulary is preserved (skill mastery vs gig progression; `gigs.md` clarifies gig mastery is separate from E2.M3). ## Documentation checked | Document | Result | |----------|--------| | `docs/plans/NEO-45-implementation-plan.md` | **Matches** — kickoff decisions, frozen ids, technical approach, acceptance checklist, and verification steps align with `content/mastery/prototype_salvage_mastery.json`, `content/schemas/mastery-catalog.schema.json`, and `scripts/validate_content.py` behavior. | -| `docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md` | **Partially matches** — freeze table, designer note, gig/skill separation, and artifact paths are correct; **Status** still **Planned**; **Data contract** omits top-level `perks` map shape. | -| `docs/plans/E2M3-pre-production-backlog.md` | **Partially matches** — E2M3-01 scope matches NEO-45; **acceptance criteria checkboxes for E2M3-01 remain unchecked** despite landed catalog/CI. | +| `docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md` | **Matches** — freeze table, designer note, **`perks` map** / `tiers[]` shape, **In Progress** status, NEO-46 handoff, NEO-45 acceptance footnote. | +| `docs/plans/E2M3-pre-production-backlog.md` | **Matches** — E2M3-01 acceptance criteria marked done (NEO-45); E2M3-02 points at NEO-45 gate constants. | | `docs/decomposition/modules/CT_M1_ContentValidationPipeline.md` | **Matches** — mastery paragraph matches script gates (schema, `skillId`, duplicate `perkId`, branch integrity, salvage-only Slice 4). | | `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **Matches** — E2.M3 row correctly states NEO-45 landed and follow-on NEO-46–49 planned. | -| `docs/decomposition/modules/module_dependency_register.md` | **Partially matches** — E2.M3 note and Linear ordering are current; register **Status** column still **Planned** vs alignment **In Progress**. | +| `docs/decomposition/modules/module_dependency_register.md` | **Matches** — E2.M3 **In Progress**, Depends On **E2.M1, E2.M2**, NEO-45 landed in note. | | `docs/decomposition/epics/epic_02_skills_and_progression.md` | **Matches** — E2.M3 / Slice 4 pointers, skill-vs-gig XP split, and salvage flagship scope. | | `docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md` | **Matches** — E2.M3 cross-link for level-up re-evaluation; no mastery XP bar implied. | | `content/README.md` | **Matches** — `mastery/` row, Slice 4 freeze, validate command; anchors target E2.M3 designer + freeze headings. | | `content/schemas/mastery-catalog.schema.json` | **Matches** — documents E2.M3 module; `schemaVersion`, `perks`, `tracks`, `tierIndex`, stable id patterns. | -| `content/mastery/prototype_salvage_mastery.json` | **Matches** — one `salvage` track; tier 1 empty `perkIds`; tier 2 perks; frozen ids per module freeze table. | +| `content/mastery/prototype_salvage_mastery.json` | **Matches** — one `salvage` track; tier 1 empty `perkIds`; tier 2 perks + branch `displayName`; frozen ids per module freeze table. | | `docs/game-design/skills.md` | **Matches** — Mastery and perks section points to E2.M3; respec open; gig tree separate. | | `docs/game-design/progression.md` | **Matches** — skill vs gig vocabulary; no mastery XP conflation. | -| `docs/game-design/gigs.md` | **Partially matches** — per-gig mastery called **open**; line referencing “E2.M3-style expression” for **gig** mastery is easy to misread as the E2.M3 **module** (skill-only). | +| `docs/game-design/gigs.md` | **Matches** — gig mastery **open**; line clarifies analogy to skill branch/perk depth, not E2.M3 module. | | `docs/decomposition/modules/E2_M1_SkillDefinitionRegistry.md` | **N/A** — no mastery-specific change required; `salvage` id exists for cross-check. | | `scripts/validate_content.py` | **Matches** — docs describe gates implemented in `_validate_mastery_catalogs` / `_prototype_slice4_gate`. | @@ -41,20 +41,20 @@ NEO-45 documentation is largely coherent with shipped content: the implementatio ## Suggestions -1. **E2.M3 module + register status** — Set **Status** to **In Progress** in `E2_M3_MasteryAndPerkUnlocks.md` (summary table) and `module_dependency_register.md` (E2.M3 row), mirroring E2.M1 after NEO-33 and the alignment doc **In Progress** row. Reserve **Done** for when NEO-49 closes the Slice 4 engine/HTTP/telemetry slice. +1. ~~**E2.M3 module + register status** — Set **Status** to **In Progress** in `E2_M3_MasteryAndPerkUnlocks.md` (summary table) and `module_dependency_register.md` (E2.M3 row), mirroring E2.M1 after NEO-33 and the alignment doc **In Progress** row. Reserve **Done** for when NEO-49 closes the Slice 4 engine/HTTP/telemetry slice.~~ **Done.** -2. **E2M3-pre-production-backlog E2M3-01 checkboxes** — Mark the three E2M3-01 acceptance criteria `[x]` when NEO-45 is merged/Done in Linear, or add a one-line note under E2M3-01: “Catalog + CI: **NEO-45** (see implementation plan).” +2. ~~**E2M3-pre-production-backlog E2M3-01 checkboxes** — Mark the three E2M3-01 acceptance criteria `[x]` when NEO-45 is merged/Done in Linear, or add a one-line note under E2M3-01: “Catalog + CI: **NEO-45** (see implementation plan).”~~ **Done.** -3. **E2.M3 Data contract — catalog shape** — Add a short subsection (or extend **Data contract**) documenting the shipped JSON layout: top-level **`perks`** map (keys = `perkId`), **`tracks`** array, **`MasteryTrack.tiers[]`** with `tierIndex` / `requiredLevel` / `branches[]` / `perkIds[]`. Link `content/schemas/mastery-catalog.schema.json` and note map-key === `PerkDef.id` (enforced in CI). +3. ~~**E2.M3 Data contract — catalog shape** — Add a short subsection (or extend **Data contract**) documenting the shipped JSON layout: top-level **`perks`** map (keys = `perkId`), **`tracks`** array, **`MasteryTrack.tiers[]`** with `tierIndex` / `requiredLevel` / `branches[]` / `perkIds[]`. Link `content/schemas/mastery-catalog.schema.json` and note map-key === `PerkDef.id` (enforced in CI).~~ **Done.** -4. **NEO-46 plan handoff** — When kicking off NEO-46, reference NEO-45 plan **gate constants** (salvage-only track count, branch-set equality tier-to-tier) so server loader parity stays explicit (already listed as a risk in the NEO-45 plan). +4. ~~**NEO-46 plan handoff** — When kicking off NEO-46, reference NEO-45 plan **gate constants** (salvage-only track count, branch-set equality tier-to-tier) so server loader parity stays explicit (already listed as a risk in the NEO-45 plan).~~ **Done.** E2.M3 **NEO-46 handoff** + E2M3-02 backlog pointer; NEO-45 plan open questions. -5. **`gigs.md` gig-mastery line** — Clarify “E2.M3-style expression” means **analogous branch/perk depth for gigs**, not the E2.M3 **skill** module; optional cross-link to a future gig-mastery epic/module when named. +5. ~~**`gigs.md` gig-mastery line** — Clarify “E2.M3-style expression” means **analogous branch/perk depth for gigs**, not the E2.M3 **skill** module; optional cross-link to a future gig-mastery epic/module when named.~~ **Done.** ## Nits -- Tier 2 branches in `prototype_salvage_mastery.json` omit `displayName` while tier 1 includes it — valid per schema; optional designer note that `displayName` is optional per branch row. -- Module **Acceptance criteria (E2.M3 / Slice 4)** bullets describe **runtime** unlock behavior (NEO-47+); consider a footnote that NEO-45 satisfies **data + CI** only. +- ~~Tier 2 branches in `prototype_salvage_mastery.json` omit `displayName` while tier 1 includes it — valid per schema; optional designer note that `displayName` is optional per branch row.~~ **Done.** Catalog + designer note. +- ~~Module **Acceptance criteria (E2.M3 / Slice 4)** bullets describe **runtime** unlock behavior (NEO-47+); consider a footnote that NEO-45 satisfies **data + CI** only.~~ **Done.** ## Verification