Skip to content

refactor(config): type grid-style colors, fold validation (#118) - #178

Merged
mrcsin merged 1 commit into
masterfrom
grid-style-color-typing
Aug 6, 2026
Merged

mrcsin merged 1 commit into
masterfrom
grid-style-color-typing

Conversation

@mrcsin

@mrcsin mrcsin commented Aug 6, 2026

Copy link
Copy Markdown
Member

Problem

The grid-style config layer restated its key structure three times: 53 raw #RRGGBB strings on the group records, a 242-line color-enumerating GridStyleValidator, and the mappers' string plumbing. "Is this hex valid" was asked far from where the string is parsed, and a color was an untyped string everywhere it flowed. Slice 4 of the #118 debloat types the colors and moves validation to the one place hex is parsed.

What changed

  • StyleColor — a readonly record struct in Core (A/R/G/B channels, Avalonia-free) with Parse/TryParse/ToString. ToString emits #RRGGBB when opaque and #AARRGGBB otherwise, uppercase; Parse accepts exactly 6 or 8 hex digits and rejects the #RGB/#ARGB shorthand the file header wrongly advertised (the validator always rejected it too).
  • 53 color fields → StyleColor across the group records. PaletteBrushFactory and the view model convert channel-wise through a new StyleColorConversions; HexColor is deleted.
  • Validation folded into the load mapper — GridStyleMapper.Map now returns Result<GridStyleOptions> and accumulates per-key errors: the same typed errors the deleted GridStyleValidator emitted, keyed by the same section path + key name, aggregated (not fail-fast), in the same emission order. Both call sites (ConfigFacade startup, GridStyleEditorFacade) map through the one seat; the facade's Validate is a deliberate vacuous pass-through kept until slice 5. GridStyleValidator (242 lines) is deleted.
  • Header + doc fixes — both shipped config headers (MBE, MOCVD) drop the #RGB/#ARGB shorthand claim (comment-only; the writer copies headers forward, so the lie would have persisted); the architecture doc's load-pipeline description follows the new one-pass mapper.

Out of scope by design (slice 5): the view-model split, grouped AXAML, and trimming the vacuous Validate off the interface. The DTOs keep their string properties, so the on-disk YAML is unchanged.

Verification

  • dotnet build SemiStep.slnx — 0 warnings; dotnet test — 1716 passed, 0 failed; dotnet format clean.
  • Error identity preserved — the error tests were retargeted from GridStyleValidator.Validate(dto) to GridStyleMapper.Map(dto) with byte-identical assertion bodies; the untouched localization + Russian-culture end-to-end message test pass unchanged. Diffed the deleted validator against the new mapper line-by-line: same error types, same section/key strings, same emission order.
  • Aggregation + the untested identity path proven by scratch checks (reverted) and now a committed regression test Validate_NullCells_FailsWithExactlyThreeSectionErrors — a null Cells yields exactly colors.cells.execution/readonly/disabled and no bare colors.cells error (a path the entire prior suite never exercised).
  • Lossless round-trip — StyleColor.Default is byte-equal to the old strings; SaveThenLoad_DistinctFixture (record→dto→yaml→dto→record) green. The one behavior change is hex case normalizing to uppercase on save, which the editor already does by rewriting the whole file.
  • Review chain: 5 comprehensive + 2 critical re-check + 2 final critical, all OUTCOME: ACHIEVED, no critical/major survived. Fixes applied: 5 doc self-contradictions, the null-Cells regression test, 3 StyleColor edge cases, the hand-rolled hex parse simplified to byte.TryParse(AllowHexSpecifier), 2 comment trims.
  • Manual test (operator): confirmed.

Slice 4 of #118 (stays open). Next: slice 5 splits the view model into per-group drafts + grouped AXAML, and closes #118.

Per-task commits before collapse
a2cdd3a docs(plan): record exec branch and verify steps
1f7792f docs(config): trim restatement in grid-style mapper comments
cfd5963 fix: address slice-4 review findings
b382fe2 docs(config): correct grid-style color-format header claims
00c88f5 refactor(config): fold grid-style validation into load mapper
f7fd631 refactor(config): type grid-style colors as StyleColor
b7dab17 feat(config): add StyleColor value type

🤖 Generated with Claude Code

The flat validation stack maintained the grid-style key structure three
times: 53 raw #RRGGBB strings on the records, a 242-line color-enumerating
validator, and the mappers' string plumbing. Slice 4 of the #118 debloat
replaces the strings with a typed value and moves validation to the one
place hex is parsed, deleting the standalone validator.

- StyleColor: a readonly record struct in Core (A/R/G/B, Avalonia-free)
  with Parse/TryParse/ToString. ToString emits #RRGGBB when opaque and
  #AARRGGBB otherwise, uppercase; Parse accepts exactly 6 or 8 hex digits
  and rejects the #RGB/#ARGB shorthand the file header wrongly advertised.
- The 53 color fields across the group records become StyleColor.
  PaletteBrushFactory and the view model convert channel-wise via a new
  StyleColorConversions; HexColor is deleted.
- GridStyleMapper.Map now returns Result<GridStyleOptions> and accumulates
  per-key errors in the load mapper: the same typed errors the deleted
  GridStyleValidator emitted, keyed by the same section path and key name,
  aggregated not fail-fast, in the same emission order. ReasonLocalizer
  output and the error tests are unchanged. GridStyleValidator is deleted;
  its two call sites (ConfigFacade startup, GridStyleEditorFacade) map
  through the same seat. The facade's Validate is a vacuous pass-through
  kept until slice 5 trims the interface.
- Both shipped config headers drop the #RGB/#ARGB shorthand claim; the
  architecture doc's pipeline description follows the new one-pass mapper.

The DTOs keep their string properties, so the on-disk YAML is unchanged;
StyleColor.Default values are byte-equal to the old strings. The one
behavior change is that hex case normalizes to uppercase on save, which
the editor already does by rewriting the whole file. #118 stays open;
slice 5 splits the view model and closes it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mrcsin
mrcsin marked this pull request as ready for review August 6, 2026 09:09
@mrcsin
mrcsin merged commit 86b7a44 into master Aug 6, 2026
1 check passed
@mrcsin
mrcsin deleted the grid-style-color-typing branch August 6, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GridStyleEditorViewModel: ~60 fields maintained in three places; sync file I/O on the UI thread

1 participant