8971912d9e
The old avatar-palette test only did expect(["white","black"]).toContain( entry.text), which can never fail (text is typed "white"|"black" and always assigned) — so the load-bearing property "all 20 colors are readable" was only really checked for the single golden name. A generator bug producing a low-contrast or out-of-gamut slot would survive the suite. Export the four existing color-math helpers (oklchToSrgb, isInGamut, relativeLuminance, contrastRatio — no logic change) and assert, for EVERY PALETTE entry: - (a) real contrast of the chosen text on the entry hex >= 3 (the code's threshold), scale-matched (hex 0..255 → /255 before relativeLuminance). Since buildPalette PREFERS white and only falls back to black when white fails 3:1, the test also asserts: if text=="black" then white's contrast is < 3 (black was mandatory) — matching the code's actual decision, not a max-contrast pick. - (b) the OKLCH is in sRGB gamut post-clamp: isInGamut(oklchToSrgb(L,C,h)). Demonstrated non-vacuous: a light bg mislabeled text:"white" → chosen contrast 1.67 (< 3) fails; an out-of-gamut component fails isInGamut. Golden-name and minPairwiseDistance tests untouched. vitest: 15 passed. No palette/hash/consumer logic changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>