5.1 KiB
Code review — NEO-46 mastery catalog startup load
Date: 2026-05-17
Scope: Branch NEO-46-mastery-catalog-startup-load · commits 898f935–174c390 vs main
Base: main
Follow-up: Addressed review suggestions (tests, validate_content.py tierIndex gate, E2.M3 module doc).
Verdict
Approve with nits
Summary
This change adds fail-fast loading of content/mastery/*_mastery.json at server startup, mirroring the NEO-34 skill-catalog pattern: path discovery, JsonSchema.Net validation, post-schema gates aligned with scripts/validate_content.py, a server-only tierIndex 1..N gate, cross-check against loaded skill ids, and a read-only IMasteryCatalogRegistry for NEO-47+. DI wires mastery load after SkillDefinitionCatalog; Program.cs eagerly resolves both catalogs before mapping routes. Tests cover the loader happy path, several deny paths, and host startup success/failure; all 13 mastery-related tests pass locally. Overall risk is low: read-only content load, no player state or HTTP surface yet.
Documentation checked
| Document | Result |
|---|---|
docs/plans/NEO-46-implementation-plan.md |
Matches — Game/Mastery/, config keys, loader gates, registry API, eager resolve, README, alignment table, acceptance checklist marked complete. |
docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md |
Matches — Status and NEO-46 handoff updated after follow-up. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
Matches — E2.M3 row updated for NEO-46 landed. |
docs/plans/NEO-45-implementation-plan.md |
Matches — Slice 4 constant/gate parity via PrototypeSlice4MasteryCatalogRules. |
scripts/validate_content.py |
Matches — tierIndex 1..N gate added in CI (follow-up). |
Register/tracking: E2.M3 alignment row is updated; module doc refreshed (follow-up).
Blocking issues
None.
Suggestions
Test coverage vs plan table — The implementation plan lists deny cases for malformed JSON, schema violation, duplicate globalDone. Added loader tests for malformed JSON, schema violation, duplicate perk reference, branch-set mismatch, flatperkIdsreference, branch-set mismatch, non-increasingrequiredLevel, Slice 4 wrongskillId, and duplicatetierIndex. Current tests cover unknown skill, duplicate perk across files, orphan perk, Slice 4 multi-track,tierIndexgap, and host negative startup; adding 2–3 focused loader tests (e.g. duplicate branch perk reference,tierIndexduplicate, wrong soleskillIdfor Slice 4) would close the gap and guard C#/Python drift.requiredLevel, wrong Slice 4 soleskillId, and duplicatetierIndex.Registry enumeration —Done.GetTracksInSkillIdOrder()/GetPerksInIdOrder()are part of the contract but untested; a small unit test on a constructed catalog would lock ordering for NEO-47 consumers.MasteryCatalogRegistryTestscovers both enumeration methods.E2.M3 module doc — Update the NEO-46 handoff bullet to state thatDone.tierIndexuniqueness and sequential 1..N are enforced at server boot (not merely “consider”), and refresh the module Status line to mention NEO-46 (optional but keeps decomposition docs authoritative).E2_M3_MasteryAndPerkUnlocks.mdupdated.CI follow-up (already in plan risks) — Consider adding theDone.tierIndexgate tovalidate_content.pyso PR CI and server boot cannot diverge on catalog edits._tier_index_gateinvalidate_content.pymirrorsMasteryCatalogLoader.TryGetTierIndexGateError.
Nits
Nit:Done.MasteryCatalogLoaderTests.CreateTempContentLayout()leaves temp directories behind; considertry/finallywithDirectory.Delete(..., recursive: true)orIDisposablefixture (same pattern as other catalog tests if they exist).TempContentLayoutimplementsIDisposable.Nit: Host startup is exercised in bothDone. Host success path lives only inMasteryCatalogLoaderTestsandMasteryCatalogRegistryTests; consolidating to one host integration test would reduce duplication (not required for merge).MasteryCatalogRegistryTests; loader tests keep host negative startup only.- Nit: Bruno
mastery-catalogfolder only hits/health— appropriate for NEO-46 scope; NEO-48 will own real mastery HTTP Bruno coverage. Deferred — out of scope for NEO-46.
Verification
# From repo root
dotnet test NeonSprawl.sln --filter "FullyQualifiedName~MasteryCatalog"
# Full server test suite (recommended before merge)
dotnet test NeonSprawl.sln
# Content gate (should stay green with repo catalog)
python3 scripts/validate_content.py
Manual: start server from clone with default content discovery; confirm Information log lists mastery directory, track count 1, perk count 2 for prototype salvage catalog.