- Float epsilon configurable (default 6) pass - JSON-path mask scoping pass with regression trap verified - 77/77 tests, 3 follow-ups marked done Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.5 KiB
3.5 KiB
normalizer-followups — Evaluation
Verdict: PASS
Generator commit: eeee3c2
Evaluator date: 2026-04-07
Verdict table
| # | Criterion | Evidence | Result |
|---|---|---|---|
| 1 | dotnet build recordingtest.sln — 0 warn / 0 err |
Build succeeded, 0 Warning(s), 0 Error(s) | pass |
| 2 | dotnet test total 77 pass |
16+17+16+5+5+6+6+6 = 77 passed, 0 failed | pass |
| A1 | Profile.FloatDecimals int? with YAML alias float_decimals |
Profile.cs:14-15 [YamlMember(Alias="float_decimals")] public int? FloatDecimals |
pass |
| A2 | RoundFloatsInNode accepts decimals parameter |
Rules.cs:102 RoundFloatsInNode(JsonNode?, int decimals) + default overload using DefaultFloatDecimals |
pass |
| A3 | DefaultFloatDecimals = 6 |
Rules.cs:97 public const int DefaultFloatDecimals = 6 |
pass |
| A4 | Profile decimals flows via Normalizer |
Normalizer.cs:95 profile.FloatDecimals ?? Rules.DefaultFloatDecimals |
pass |
| A5 | Omitted float_decimals defaults to 6 |
Test Profile_OmittedFloatDecimals_DefaultsTo6 asserts profile.FloatDecimals == null and output rounds to 3.141593 |
pass |
| A6 | Configurable decimals actually applied | Test RoundFloats_ProfileWithDecimals3_RoundsTo3 writes temp profile, expects 3.142 |
pass |
| B1 | ParseJsonPathLite exists, rejects * and [...] |
Rules.cs:200-222 throws on wildcards/indexers, requires leading $. |
pass |
| B2 | MaskVolatileSettings(node, paths) walks with path stack |
Rules.cs:227-289 pre-parses allowlist, maintains stack list, exact-chain compare in PathMatches() |
pass |
| B3 | DefaultVolatileSettingPaths has 16 entries |
Rules.cs:176-194 — counted 16 paths |
pass |
| B4 | default.yaml migrated to list form |
profiles/default.yaml:10-26 — YAML sequence of 16 $.<path> strings; float_decimals: 6 present |
pass |
| B5 | Regression trap: SameNameInUnrelatedSubtree_NotMasked |
RuleTests.cs:174-183 — input {"GridSnap":true,"Foo":{"GridSnap":false}} with ["$.GridSnap"]; asserts root masked and n["Foo"]["GridSnap"].GetValue<bool>() == false. Pre-fix name-based fallback would have masked both, causing GetValue<bool>() to throw InvalidOperationException on <VOLATILE> string → test is load-bearing |
pass |
| B6 | Nested path mask works | MaskVolatileSettings_NestedPath_MasksCorrectly — $.GridColor.R masks only R, leaves G |
pass |
| B7 | Root mask works | MaskVolatileSettings_RootField_Masks |
pass |
| B8 | No leftover VolatileSettingFieldNames fallback |
Grep in src/ — no matches anywhere |
pass |
| 9 | CoverageTests still green | Normalizer.Tests dll 16 passed (includes coverage tests) | pass |
Notes
- Regression trap load-bearing: confirmed. The old
VolatileSettingFieldNames.Contains(kv.Key)approach would mask bothGridSnapoccurrences → nested.GetValue<bool>()on a"<VOLATILE>"JsonValue would throw. The test would fail loudly. - Test count for Normalizer.Tests went from 10 → 16 as claimed (6 new tests present and accounted for).
- Default-on behavior preserved:
default.yamlboth specifiesfloat_decimals: 6explicitly AND the omitted-profile test proves the?? DefaultFloatDecimalsfallback path. - Count of
DefaultVolatileSettingPaths: 16 entries confirmed (CanOverrideWireColorWithFace, IsSidePanelVisible, OverrideFaceColor, Solar_IsLocalTime, VisibleGrid, GridSnap, MidpointOsnap, GridSpacing, GridColor.{ALPHA,BLUE,GREEN,RED}, MajorGridColor.{ALPHA,BLUE,GREEN,RED}).
Partial / gaps
None. Both follow-ups are complete with no residual fallback code.