DS3 (design): the color contract — one meaning, enforced by tests

Signal-green #00D4A0 now means exactly ONE thing (edge/active/A-tier/CTA),
locked by tests that fail on violation. Part 1 of DESIGN-SPEC v2.

- web/src/lib/colorContract.js — pure CommonJS helpers: edgeColor(value)
  colors edge/CLV/delta by SIGN (neg=var(--miss), pos=var(--g-a), 0=neutral);
  gradeTierColor() (A/A+ green, B blue, C amber, D/F red, in lockstep with
  vyndrTokens.gradeColor); gradeGlows() (A/A+ only); deltaE()/isSignalGreen()
  CIE76 gate so no archetype hue dilutes the signal.
- GradeResultCard: edge confidence-strip + EDGE row route through edgeColor
  (a -33.3% edge was rendering GREEN — audit #3); grade-hero glow gated to
  A/A+ via gradeGlows (a glowing C devalued the cue); VYNDR INTELLIGENCE
  panel de-flooded (neutral border, Form/Rest neutral not green — #15).
- LiveHeroProp: negative edge now muted red, not neutral (sign completeness).
- Archetype dedup off signal-green (both archetypes.js + archetypeService.js,
  kept matched): DUAL THREAT/MOTOR #00D4A0, MIRROR #34D399, ARTILLERY/RANGE/
  GHOST/BLADE #2DD4BF, BRUSH #3DDC84 shifted to distinct non-green hues (all
  ΔE>=44 from #00D4A0). Within-sport uniqueness preserved.
- tests/unit/colorContract.test.js — 21 tests: helper units + source-grep
  violation locks + archetype-green-dedup. QA.20-22 kept green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-12 19:34:12 -04:00
parent 24af247b29
commit 49a3323c20
6 changed files with 293 additions and 26 deletions
+8 -8
View File
@@ -37,7 +37,7 @@ const ARCHETYPES = {
education: 'A Fortress generates value on the defensive glass and at the rim. Rebounds and blocks are matchup-resilient; their scoring depends on how much offense flows their way.',
},
ARTILLERY: {
legacyName: 'STRETCH BIG', tag: 'ARTILLERY', sport: 'nba', color: '#2DD4BF', glyph: 'target',
legacyName: 'STRETCH BIG', tag: 'ARTILLERY', sport: 'nba', color: '#159AAE', glyph: 'target',
description: 'Floor-spacing shooting big',
propDNA: { reliable: ['threes', 'rebounds'], volatile: ['assists', 'blocks'] },
education: 'Artillery bigs space the floor and crash the glass. Threes and rebounds are their bread and butter; assists and blocks are situational.',
@@ -49,7 +49,7 @@ const ARCHETYPES = {
education: 'A Surge soaks up shots when a star sits or is injured. Their points props spike on cascade nights — read the injury report before trusting the baseline.',
},
'DUAL THREAT': {
legacyName: 'COMBO GUARD', tag: 'DUAL THREAT', sport: 'nba', color: '#00D4A0', glyph: 'twin',
legacyName: 'COMBO GUARD', tag: 'DUAL THREAT', sport: 'nba', color: '#1EA6D8', glyph: 'twin',
description: 'Scoring + playmaking hybrid',
propDNA: { reliable: ['points', 'assists'], volatile: ['rebounds'] },
education: 'Dual Threats score and create in equal measure, so points and assists both stay in play. Rebounds are the volatile leg for their size.',
@@ -123,7 +123,7 @@ const ARCHETYPES = {
education: 'Shields contribute on both ends. Points and defensive stats stay live; their playmaking is secondary.',
},
RANGE: {
legacyName: 'STRETCH FORWARD', tag: 'RANGE', sport: 'wnba', color: '#2DD4BF', glyph: 'target',
legacyName: 'STRETCH FORWARD', tag: 'RANGE', sport: 'wnba', color: '#159AAE', glyph: 'target',
description: 'Floor-spacing forward',
propDNA: { reliable: ['threes', 'points'], volatile: ['assists', 'blocks'] },
education: 'Range forwards space the floor from the four. Threes and points are reliable; interior props are matchup-dependent.',
@@ -149,7 +149,7 @@ const ARCHETYPES = {
education: 'Bombers drive the ball over the fence. Total bases and home-run props carry their value; their batting-average-driven props (hits) are the volatile leg from strikeout risk.',
},
BRUSH: {
legacyName: 'CONTACT', tag: 'BRUSH', sport: 'mlb', color: '#3DDC84', glyph: 'crosshair',
legacyName: 'CONTACT', tag: 'BRUSH', sport: 'mlb', color: '#C0C63C', glyph: 'crosshair',
description: 'High average, low strikeout',
propDNA: { reliable: ['hits'], volatile: ['home_runs', 'total_bases'] },
education: 'Brush hitters rarely strike out, so their hits props are among the most reliable in baseball. Power props (HR, TB) are the volatile leg.',
@@ -173,7 +173,7 @@ const ARCHETYPES = {
education: 'Whiffs rack up strikeouts but allow more baserunners than an Alpha. The strikeout prop is reliable; earned-run and innings props swing with their traffic.',
},
GHOST: {
legacyName: 'SPEED THREAT', tag: 'GHOST', sport: 'mlb', color: '#2DD4BF', glyph: 'chevrons',
legacyName: 'SPEED THREAT', tag: 'GHOST', sport: 'mlb', color: '#159AAE', glyph: 'chevrons',
description: 'Stolen bases, speed score',
propDNA: { reliable: ['stolen_bases', 'runs'], volatile: ['home_runs'] },
education: 'Ghosts turn singles into runs. Stolen-base and runs props are their lane; power props rarely clear.',
@@ -203,7 +203,7 @@ const ARCHETYPES = {
education: 'Catalysts get on base and score. Hits and runs props are their lane; RBI and power props sit lower in their profile.',
},
MIRROR: {
legacyName: 'GAP HITTER', tag: 'MIRROR', sport: 'mlb', color: '#34D399', glyph: 'uparrow',
legacyName: 'GAP HITTER', tag: 'MIRROR', sport: 'mlb', color: '#8FC04A', glyph: 'uparrow',
description: 'Gap-to-gap line-drive bat',
propDNA: { reliable: ['total_bases', 'hits'], volatile: ['home_runs'] },
education: 'Mirror hitters spray doubles to both gaps. Total bases and hits are reliable; home-run props are the volatile leg of their extra-base profile.',
@@ -253,13 +253,13 @@ const ARCHETYPES = {
education: 'Towers win aerial duels and hold the ball up. Shot props are reliable; conversion to goals is variable.',
},
MOTOR: {
legacyName: 'BOX-TO-BOX', tag: 'MOTOR', sport: 'soccer', color: '#00D4A0', glyph: 'chevrons',
legacyName: 'BOX-TO-BOX', tag: 'MOTOR', sport: 'soccer', color: '#1EA6D8', glyph: 'chevrons',
description: 'All-action central midfielder',
propDNA: { reliable: ['tackles', 'passes'], volatile: ['goals', 'shots'] },
education: 'Motors cover every blade of grass. Tackles and passing props are reliable; their attacking output swings by role.',
},
BLADE: {
legacyName: 'WING WIZARD', tag: 'BLADE', sport: 'soccer', color: '#2DD4BF', glyph: 'slash',
legacyName: 'WING WIZARD', tag: 'BLADE', sport: 'soccer', color: '#159AAE', glyph: 'slash',
description: 'Dribbling wide threat',
propDNA: { reliable: ['shots', 'assists'], volatile: ['goals'] },
education: 'Blades beat defenders wide. Shots and assists are their lane; goals come in streaks.',