Skip to content

test(mindmap): #204 pin HLSColor lighter/darker readability contract (20 tests) - #573

Merged
jsboige merged 1 commit into
masterfrom
test/204-hlscolor-pure-contract
Jun 21, 2026
Merged

test(mindmap): #204 pin HLSColor lighter/darker readability contract (20 tests)#573
jsboige merged 1 commit into
masterfrom
test/204-hlscolor-pure-contract

Conversation

@jsboige

@jsboige jsboige commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Pin the mind-map node readability color contract for HLSColor.GetLighterColor(string) and GetDarkerColor(string) — pure string→string color transforms with zero coverage, used in 6 production call sites.

Why

The mind-map builder derives two shades from each fallacy family's color:

  • GetLighterColor(string)node.BACKGROUND_COLOR (a lighter tint = the node fill)
  • GetDarkerColor(string)node.COLOR (a darker shade = the node text)

Both are called from SetNodeStyle in MindMapDocumentConfig, FallacyMindMapDocumentConfig, and VirtueMindMapDocumentConfig (6 call sites total). Together they enforce the readability invariant: on a family-colored node, the text is darker and the fill is lighter than the raw family color.

A regression that flipped a direction — darker becoming lighter than the base, or lighter becoming darker — would make every node's text unreadable (dark-on-dark / light-on-light). Because mind-map rendering is currently gated behind visual QA the cluster cannot always run (the #565 pixel-RTL wall), this is exactly the silent-aesthetics regression a contract test must catch in isolation.

Tests added (20)

Pure & deterministic; additive only (no production code changed). Luminance proxy = Rec.601 weights (0.299R+0.587G+0.114B), a stable viewer-independent direction measure:

  1. Darker is darkerGetDarkerColor yields lower luminance than base, across 5 real family colors (#811da3 purple, #555555 gray, #08af93 turquoise, #8dc801 green, #dc0f0a red) — the text half
  2. Lighter is lighterGetLighterColor yields higher luminance than base, same 5 colors — the fill half
  3. Both return valid parseable hex — the .mm node holds the string verbatim into the XML; an invalid value corrupts the file
  4. Determinism — same family color → same shade (builder calls once per node)
  5. Transforms change the color — anti no-op regression (output ≠ input for a mid-range color)
  6. Lighter is lighter than darker for the same base — the relative ordering the light-fill/dark-text scheme assumes
  7. Characterization — exact observed outputs for purple #811da3 (lighter #CB76E7 / darker #450F57), captured from the real ColorTranslator HLS round-trip — catches a vendored-HLS refactor

Verification

  • dotnet test --filter HlsColorContractTests20/20 pass
  • Full suite → 506/0/5 (baseline 486 + 20). 0 regression.
  • Build: 0 error (only preexisting nullable warnings).

Verify-before-code note

ColorExtensions.GetContrast (+ its helpers ConvertToHSB/ConvertToRGB) in the same file are dead code (defined, never called in production — grep confirms zero call sites outside the file). Excluded from this PR; flagging for a separate cleanup if desired. This PR targets only the live GetLighterColor/GetDarkerColor path.

Context

Cluster is degraded (po-2023 down, DNN/régén orphaned). #204 pure-helpers lane is closed; this PR extends coverage into the mind-map rendering zone — relevant given #565 (8-lang régén) is pending and #568 (mindmap reliability) is open. The readability invariant is precisely the contract a worker can pin while visual QA stays ai-01-only.

…(20 tests)

Pin the mind-map node readability color contract surfaced by the #204 coverage sweep.

HLSColor.GetLighterColor(string) and GetDarkerColor(string) are pure string→string color
transforms (ColorTranslator.FromHtml/ToHtml, no I/O) used in 6 production call sites: the
mind-map builder derives, from each fallacy family color, a lighter tint (node BACKGROUND_COLOR
fill) and a darker shade (node COLOR text). Together they enforce the READABILITY invariant:
on a family-colored node, the text is darker and the fill is lighter than the raw family color.

A regression that flipped a direction — darker lighter than base, or lighter darker — would make
every node's text unreadable (dark-on-dark / light-on-light). Because mind-map rendering is
currently gated behind visual QA the cluster cannot always run, this is exactly the silent-
aesthetics regression a contract test must catch in isolation.

Pure & deterministic. Had ZERO coverage (confirmed: no reference in the test project).

Pinned (output-neutral, additive tests only — no production code changed), 20 tests:
- GetDarkerColor yields LOWER luminance than base, across 5 real family colors (incl. dark gray
  #555555, purple #811da3, red/green/turquoise) — the text-readability half
- GetLighterColor yields HIGHER luminance than base, same 5 colors — the fill half
- Both return valid parseable hex (the .mm node holds the string verbatim into the XML)
- Determinism (same family color → same lighter/darker shade)
- Transforms produce shades distinct from base (anti no-op regression)
- Lighter is lighter than darker for the same base (relative ordering the scheme assumes)
- Characterization: exact observed outputs for purple #811da3 (lighter #CB76E7 / darker #450F57)
  captured from the real ColorTranslator HLS round-trip — catches a vendored-HLS refactor

Luminance proxy = Rec.601 weights (0.299R+0.587G+0.114B), stable viewer-independent direction
measure (not exact values).

Suite 506/0/5 (baseline 486 + 20).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jsboige
jsboige merged commit bfd4a84 into master Jun 21, 2026
3 checks passed
@jsboige
jsboige deleted the test/204-hlscolor-pure-contract branch June 21, 2026 11:08
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.

1 participant