5.6 KiB
Code review — NEO-145 (E7M4-02 server contract template catalog load)
Date: 2026-06-20
Scope: Branch NEO-145-e7m4-02-server-contract-template-catalog-load-fail-fast — commits 215493f…937412e (3 commits)
Base: main
Verdict
Approve with nits
Summary
NEO-145 delivers fail-fast startup loading of content/contracts/*_contract_templates.json with CI-parity gates ported to C# (PrototypeE7M4ContractCatalogRules), JSON Schema validation with quest/faction bundle $refs, duplicate-id rejection, and cross-ref checks against encounter, faction, frozen prototype item/skill catalogs, and the E7M2 mission_reward allowlist. IContractTemplateRegistry + DI registration follow established faction/encounter patterns; Program.cs registers and eagerly resolves the catalog after the quest catalog. Tests cover loader happy/negative paths, registry lookups, and host boot via InMemoryWebApplicationFactory; dotnet test (839) and validate_content.py both pass. Risk is low: infrastructure-only, no player-facing HTTP yet; primary failure mode is startup exit with sorted error: lines — appropriate for this slice.
Documentation checked
| Document | Alignment |
|---|---|
docs/plans/NEO-145-implementation-plan.md |
Matches — AC checklist complete; reconciliation section reflects shipped loader, registry, DI, tests, README. |
docs/plans/E7M4-pre-production-backlog.md (E7M4-02) |
Matches — loader, registry, cross-ref, tests, README in scope; instance store/HTTP/Godot correctly out of scope. |
docs/decomposition/modules/E7_M4_ContractMissionGenerator.md |
|
docs/decomposition/modules/CT_M1_ContentValidationPipeline.md |
|
docs/decomposition/modules/documentation_and_implementation_alignment.md |
|
docs/decomposition/modules/module_dependency_register.md |
|
docs/decomposition/modules/client_server_authority.md |
N/A — read-only catalog load; no authority boundary change. |
| Full-stack epic decomposition | N/A — infrastructure-only; client counterpart correctly none (NEO-154 capstone). |
Register/tracking tables were not updated for NEO-145 in this branch — should fix (non-blocking) per plan §6 optional module note and alignment doc pattern from NEO-144. Done.
Blocking issues
None.
Suggestions
-
Update decomposition tracking for E7M4-02 — After merge, extendDone.documentation_and_implementation_alignment.mdE7.M4 row and optionallymodule_dependency_register.md/E7_M4_ContractMissionGenerator.mdwith one line: server fail-fast catalog load +IContractTemplateRegistry(NEO-145). Mirrors NEO-144 alignment follow-up. -
CT_M1 server-load paragraph — Add a sentence under the contract-catalog CI bullet that the host mirrors the same gates at startup viaDone.ContractTemplateCatalogLoader(NEO-145), matching faction/quest catalog precedent in that doc. -
Band-cap negative through full loader — Plan §5 lists band-cap violation via loader; shipped coverage testsDone.TryGetBandCapGateErrorin isolation only. Consider one temp-fixture test that writes qty 11 and assertsContractTemplateCatalogLoader.Loadthrows (same pattern as freeze/roster negatives) so the loader→rules wiring is exercised end-to-end.Load_ShouldThrow_WhenItemQuantityExceedsPrototypeBundleLimitsexercises loader→freeze gate (qty 11 fails freeze before band-cap on the single-template roster);TryGetBandCapGateError_ShouldReturnError_WhenItemQuantityExceedsBandCapcovers band-cap rule directly. -
Missing schema path test — Plan §5 lists missing schema; only missing directory is tested. A temp layout with contracts dir present but schema file removed would close the gap cheaply.Done.Load_ShouldThrow_WhenSchemaFileMissingadded.
Nits
-
Nit:Done.Load_ShouldThrow_WhenBundleItemIdUnknowncallsTryGetCrossRefErrordirectly rather thanLoad; rename toTryGetCrossRefError_ShouldReturnError_WhenBundleItemIdUnknown(or route through the loader) for clarity. -
Nit: Bruno folderDone. Plan reconciliation notes Bruno health smoke.bruno/neon-sprawl-server/contract-catalog/(health smoke) is a nice addition but not listed in the plan — harmless; optional one-line in plan reconciliation if you want doc parity. -
Nit:
AddContractTemplateCatalogre-bindsContentPathsOptions(same as faction/quest extensions) — fine; no action needed.
Verification
Commands run during review (all green):
dotnet test NeonSprawl.sln
python3 scripts/validate_content.py
Author should confirm fail-fast startup manually once (optional):
# Temporarily break encounterTemplateId in prototype_contract_templates.json
dotnet run --project server/NeonSprawl.Server # expect startup InvalidOperationException
Before merge: skim decomposition alignment suggestions above — functional code is merge-ready without them.