5.4 KiB
Code review — NEO-45 (salvage mastery catalog + schemas + CI)
Date: 2026-05-16
Scope: Branch NEO-45-salvage-mastery-catalog-schemas-ci, commit f5f04d8 (NEO-45: lock prototype salvage mastery catalog, schemas, and CI.). Issue NEO-45.
Base: main (parent of f5f04d8)
Verdict
Approve with nits
Summary
The change delivers NEO-45 as a content-only slice: mastery-catalog.schema.json, prototype prototype_salvage_mastery.json (one salvage track, tier 1 branch pick @ level 2 with empty perkIds, tier 2 perks @ level 4), extended validate_content.py with schema validation, skillId cross-check, perk map key/id alignment, duplicate perkId / reference guards, branch-set continuity across tiers, strictly increasing requiredLevel, and a Slice 4 salvage-only / exactly-one-track gate. Documentation (E2.M3 module, CT.M1, epic Slice 4, alignment table, implementation plan, backlog) is thorough and matches kickoff decisions. Risk is low: no server/C#/client surface; PR gate already runs the script. Main gap is status field drift between the alignment row (In Progress) and the register / E2.M3 Summary (Planned).
Documentation checked
| Document | Assessment |
|---|---|
docs/plans/NEO-45-implementation-plan.md |
Matches — schema shape, catalog layout, validator gates, frozen ids, out-of-scope (NEO-46+), verification steps align with the implementation. |
docs/plans/E2M3-pre-production-backlog.md |
Matches — E2M3-01 acceptance criteria marked done (NEO-45). |
docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md |
Matches — data contract (perks map + tracks), designer note, freeze table, In Progress status. |
docs/decomposition/modules/CT_M1_ContentValidationPipeline.md |
Matches — CI paragraph documents mastery validation alongside skills. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
Matches — E2.M3 row aligned with register and module 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 — Slice 4 scope, NEO-45→49 chain, E2.M4 separation. |
docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md |
Matches — NEO-43 landed line + E2.M3 level-up hook cross-link. |
content/README.md |
Matches — Slice 4 freeze reminder and links to E2.M3. |
Register / tracking: Update Planned → In Progress for E2.M3 Done (register, module Summary, backlog checkboxes).
Blocking issues
None.
Suggestions
-
Align E2.M3 status across docs —Done. Register + module Summary → In Progress; Depends On → E2.M1, E2.M2; E2.M3 note cites NEO-45 landed.documentation_and_implementation_alignment.mdmarks E2.M3 In Progress with NEO-45 landed, butmodule_dependency_register.mdandE2_M3_MasteryAndPerkUnlocks.mdSummary still say Planned. Pick one convention (recommended: In Progress on register + module Summary when catalog merges, matching E2.M1 after NEO-33). -
Backlog checkboxes — InDone.docs/plans/E2M3-pre-production-backlog.md, flip E2M3-01 acceptance criteria to[x](or add a “Done in NEO-45” note) so the backlog does not contradictNEO-45-implementation-plan.md. -
Optional CI gate: unreferenced perks —Done.perksmap entries with noperkIdsreference currently pass validation (orphan perk → exit 0). Consider rejecting deadPerkDefrows in a follow-up if designers should not ship unused defs; not required by NEO-45 AC.validate_content.pyrejects unreferencedperksmap entries per file. -
NEO-46 parity — Plan already flags mirroring Python gate constants in C# loader; when implementing NEO-46, also consider validatingDone. Noted intierIndexuniqueness / sequential 1..N per track (schema allows gaps today; catalog is correct).NEO-45-implementation-plan.mdopen questions for NEO-46.
Nits
Nit: Tier 2 branches inDone. Tier 2 branches includeprototype_salvage_mastery.jsonomitdisplayNamewhile tier 1 includes it; valid per schema but slightly uneven for authoring.displayName.Nit:Done. Comment inglobal_referenced_perk_idstreats a perk as globally single-use across branches/files — correct for mutually exclusive v1 trees, but document if a future catalog ever needs the sameperkIdon two branches (unlikely).validate_content.py.- Nit: Adding a second salvage track with duplicated perk references fails on duplicate reference errors before the Slice 4 “exactly one track” message; harmless, but authors adding a second track with no perk refs get the clearer Slice 4 error.
Verification
Run before merge:
pip install -r scripts/requirements-content.txt # if needed
python3 scripts/validate_content.py
python3 scripts/check_decomposition_language.py
Manual negative cases (restore catalog after each):
- Unknown
skillId→ non-zero, actionable stderr. - Unknown
perkIdsreference → non-zero. - Duplicate perk reference across branches → non-zero.
- Second
salvagetrack (no perk refs) → Slice 4 gate non-zero.
No dotnet test or Godot run required for this story (content-only per plan).