neon-sprawl/docs/reviews/2026-05-16-NEO-45-code-revi...

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

  1. Align E2.M3 status across docsdocumentation_and_implementation_alignment.md marks E2.M3 In Progress with NEO-45 landed, but module_dependency_register.md and E2_M3_MasteryAndPerkUnlocks.md Summary still say Planned. Pick one convention (recommended: In Progress on register + module Summary when catalog merges, matching E2.M1 after NEO-33). Done. Register + module Summary → In Progress; Depends On → E2.M1, E2.M2; E2.M3 note cites NEO-45 landed.

  2. Backlog checkboxes — In 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 contradict NEO-45-implementation-plan.md. Done.

  3. Optional CI gate: unreferenced perksperks map entries with no perkIds reference currently pass validation (orphan perk → exit 0). Consider rejecting dead PerkDef rows in a follow-up if designers should not ship unused defs; not required by NEO-45 AC. Done. validate_content.py rejects unreferenced perks map entries per file.

  4. NEO-46 parity — Plan already flags mirroring Python gate constants in C# loader; when implementing NEO-46, also consider validating tierIndex uniqueness / sequential 1..N per track (schema allows gaps today; catalog is correct). Done. Noted in NEO-45-implementation-plan.md open questions for NEO-46.

Nits

  • Nit: Tier 2 branches in prototype_salvage_mastery.json omit displayName while tier 1 includes it; valid per schema but slightly uneven for authoring. Done. Tier 2 branches include displayName.
  • Nit: global_referenced_perk_ids treats a perk as globally single-use across branches/files — correct for mutually exclusive v1 trees, but document if a future catalog ever needs the same perkId on two branches (unlikely). Done. Comment in 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 perkIds reference → non-zero.
  • Duplicate perk reference across branches → non-zero.
  • Second salvage track (no perk refs) → Slice 4 gate non-zero.

No dotnet test or Godot run required for this story (content-only per plan).