Summary
Configuration/Loaders/{Properties,Columns,Groups,Actions}SectionLoader.cs share a near-identical ~60-line skeleton (enumerate directory, load files, merge, duplicate-key detection) — verified Properties vs Columns: same code modulo DTO type and message strings. Additionally, seven loader files build the same new DeserializerBuilder().WithNamingConvention(...).IgnoreUnmatchedProperties().Build() chain.
Why it matters
Any fix to the shared skeleton (error wording, duplicate handling, file-order determinism) must be applied four times; the copies have already started to require synchronized edits.
Suggested fix
One generic SectionLoader<TDto> parameterized by DTO type, section name, and key selector; a single shared deserializer factory. Removes roughly 200 duplicated lines. The overlay-based invalid-config tests (SemiStep.Tests/YamlConfigs/Invalid/*) already cover the behavior and should pass unchanged — they are the safety net for this refactor.
Summary
Configuration/Loaders/{Properties,Columns,Groups,Actions}SectionLoader.csshare a near-identical ~60-line skeleton (enumerate directory, load files, merge, duplicate-key detection) — verified Properties vs Columns: same code modulo DTO type and message strings. Additionally, seven loader files build the samenew DeserializerBuilder().WithNamingConvention(...).IgnoreUnmatchedProperties().Build()chain.Why it matters
Any fix to the shared skeleton (error wording, duplicate handling, file-order determinism) must be applied four times; the copies have already started to require synchronized edits.
Suggested fix
One generic
SectionLoader<TDto>parameterized by DTO type, section name, and key selector; a single shared deserializer factory. Removes roughly 200 duplicated lines. The overlay-based invalid-config tests (SemiStep.Tests/YamlConfigs/Invalid/*) already cover the behavior and should pass unchanged — they are the safety net for this refactor.