normalizer: configurable float epsilon + JSON-path volatile mask scoping

Follow-ups to #4 normalizer PoC v2:
- Profile.float_decimals (default 6) flows into Rules.RoundFloatsInNode.
- mask_volatile_settings switches from name-only HashSet to a JSONPath-lite
  allowlist ($.a.b.c) so same-named fields in unrelated subtrees stay intact.
- default.yaml migrated; 6 new tests including a regression trap for the
  unrelated-subtree case. 16/16 normalizer tests, 77/77 solution tests.

Refs #2
This commit is contained in:
minsung
2026-04-07 20:42:27 +09:00
parent 0f0324efb5
commit eeee3c2a03
6 changed files with 238 additions and 26 deletions

View File

@@ -0,0 +1,28 @@
# 2026-04-07 — normalizer follow-ups (Generator)
## 작업
normalizer PoC v2(#4, `05c7a3f`)에서 Evaluator가 비차단 risk로 남긴 두 항목을 구현.
- **Follow-up A** Float epsilon 구성화: `NormalizeProfile.float_decimals` (YAML, optional, default 6) → `Rules.RoundFloatsInNode(node, decimals)` 오버로드 → `Normalizer.Normalize`가 프로파일에서 읽어 주입.
- **Follow-up B** `mask_volatile_settings` JSON-path 스코핑: 필드명 HashSet → JSONPath-lite 화이트리스트(`$.a.b.c`). `Rules.ParseJsonPathLite`로 세그먼트 파싱, 정확 경로 매칭. 같은 이름의 무관한 서브트리 보호.
## 변경 파일
- `src/Recordingtest.Normalizer/Profile.cs``FloatDecimals`, `MaskVolatileSettings` 필드 추가. `IgnoreUnmatchedProperties()`.
- `src/Recordingtest.Normalizer/Rules.cs``RoundFloatsInNode(node, decimals)`, `MaskVolatileSettings(node, IReadOnlyList<string>)`, `ParseJsonPathLite`, `DefaultVolatileSettingPaths`.
- `src/Recordingtest.Normalizer/Normalizer.cs``round_floats`/`mask_volatile_settings` 케이스에서 프로파일 옵션 전달.
- `src/Recordingtest.Normalizer/profiles/default.yaml``float_decimals: 6` + 16개 `$.<path>` 항목.
- `tests/Recordingtest.Normalizer.Tests/RuleTests.cs` — 테스트 6개 추가.
## 결과
- Build: 0 warnings, 0 errors (TreatWarningsAsErrors).
- Normalizer tests: 10 → 16 (+6 신규, 모두 green).
- 솔루션 전체: 77 passed / 0 failed.
## Regression trap (Follow-up B)
`MaskVolatileSettings_SameNameInUnrelatedSubtree_NotMasked``{GridSnap, Foo:{GridSnap}}` 입력에 `$.GridSnap` 화이트리스트를 적용. 수정 전 코드는 이름 기반 HashSet으로 `Foo.GridSnap`까지 마스킹했을 것이고 테스트가 실패했을 것이다. 신규 path 매칭은 stack 깊이/세그먼트가 정확히 일치할 때만 마스킹하므로 root 만 변경되고 nested boolean은 보존됨.
## 메타
- 소요 시간: 약 25분
- Context 사용량: 약 47k tokens (단일 세션)
- 관련 이슈: #2 (normalizer follow-ups), #4 후속
- 마커: non-issue / follow-up only — Sprint Contract DoD 변경 없음, PROGRESS/PLAN은 Evaluator/handoff에서 갱신.