3.7 KiB
Code review: NEO-35 (SkillDefinitionRegistry)
Date: 2026-05-04
Scope: Branch NEO-35-e2m1-skilldefinition-registry vs origin/main (ahead 2 commits): ISkillDefinitionRegistry, SkillDefinitionRegistry, DI registration, SkillDefinitionRegistryTests, plan docs/plans/NEO-35-implementation-plan.md, comment tweak on SkillDefinitionCatalog.
Base: origin/main
Verdict
Approve with nits
Summary
The change adds a thin registry adapter over SkillDefinitionCatalog with TryGetDefinition, ordered enumeration, XML guidance for E2.M2 and NEO-36, and singleton DI registration. Unit tests cover happy path, unknown id, id-ordered listing, loader-backed consistency, and host DI resolution. Behavior matches the NEO-35 plan and E2.M1 module intent for this slice (read-only metadata; grant enforcement remains E2.M2). dotnet test on NeonSprawl.Server.Tests passes (110 tests).
Documentation checked
| Document | Result |
|---|---|
docs/plans/NEO-35-implementation-plan.md |
Matches — interface + adapter + DI + tests align with acceptance checklist and technical approach (TryGet* semantics, SkillDefRow, enumeration, AAA tests). |
docs/decomposition/modules/E2_M1_SkillDefinitionRegistry.md |
Matches — exposes stable ids, categories, display names, allowedXpSourceKinds; documents E2.M2 validation expectation on the interface; does not implement unlock gating or XP engine (correctly out of scope). |
docs/decomposition/modules/module_dependency_register.md |
Matches — E2.M1 row describes registry / allowedXpSourceKinds; implementation fits. |
docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md (cross-read) |
Matches — interface remarks align with “reject grants when sourceKind not listed.” |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
Matches — E2.M1 snapshot row updated for NEO-35 landed. |
Blocking issues
(none)
Suggestions
-
Done.Host_ShouldResolveRegistryFromDi_WhenStartupSucceedsAAA (SkillDefinitionRegistryTests.cs) —// Actcurrently performsGetAsync("/health"),GetRequiredService<ISkillDefinitionRegistry>(), andTryGetDefinitiontogether. Per csharp-style, Act should be the behavior under test only (typically one call, or a short intentional sequence for one scenario). Prefer moving/healthto Arrange as a startup warm-up if ordering matters, dropping it ifCreateClient()already builds the host enough for the catalog, or splitting host-health vs DI-resolution into separate tests so each method has a single clear Act. -
Post-merge doc hygiene — UpdateDone.documentation_and_implementation_alignment.mdE2.M1 / NEO-35 line when this story merges so the implementation tracking table matches reality.
Nits
-
Nit:Done.finallyblock uses an emptycatch { }aroundDirectory.DeleteinTryGetDefinition_ShouldMatchLoaderCatalog_WhenUsingPrototypeFixture. csharp-style prefers avoiding empty catches; for tests, catching a specific exception type (or a tiny helper) is enough if you want to silence flaky cleanup on Windows/locked files. -
Nit:Done.TryGetDefinitionwith anullskillIdwill throw from the underlying dictionary lookup; the XML says misses do not throw. That is fine for normal string ids; if you want the contract to include null as “not found,” add an explicit guard—only worth it if public API should treat null like unknown.
Verification
dotnet test server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj— all tests passed at review time.