# Fitness Functions v0.1

Fitness functions turn design judgment into numbers. Every function has an ID, a type, a method, a normalized score from 0 to 1, and a version. Scores are only comparable within the same function version.

## Types

| Type | Cost | Runs on |
|---|---|---|
| **Gate** | Seconds | Every variant. Fail means composite = 0. |
| **Measured** | Seconds to minutes | Every variant that passes gates. |
| **Judged** | Model calls | Top 20% after measured scoring. |
| **Field** | Days, real users | Top 2 or 3 finalists, when the target allows it. |

## Gates

| ID | Check | Pass condition | Tool |
|---|---|---|---|
| `G-A11Y` | Automated accessibility | Zero critical or serious violations | axe-core |
| `G-CONTRAST` | Text and UI contrast | WCAG 2.2 AA: 4.5:1 body text, 3:1 large text and UI components | axe-core plus computed styles |
| `G-OVERFLOW` | Horizontal overflow | No horizontal scroll at 320 px width | Playwright: `scrollWidth <= clientWidth` |
| `G-RENDER` | Render health | No console errors, no failed requests | Playwright |
| `G-LOCK` | Locked genes untouched | Locked values identical to genome | Diff against `genome.json` |

## Measured functions

| ID | Measures | Method | Score |
|---|---|---|---|
| `M-TOKEN` | Token fidelity | Share of computed color, spacing, radius and font values that map to a genome token | Ratio, 0 to 1 |
| `M-RHYTHM` | Spacing rhythm | Share of vertical gaps on the spacing scale; variance of section spacing | 1 minus normalized deviation |
| `M-TYPE` | Typographic health | Body size at least 16 px, line length 45 to 75 characters, line height 1.4 to 1.7 for body | Share of text blocks in range |
| `M-TARGET` | Touch targets | Interactive elements at least 44 by 44 px at 320 and 768 px | Share passing |
| `M-PERF` | Performance | Lighthouse performance, LCP, CLS, JS weight | Weighted, normalized against budget |
| `M-RESP` | Responsive stability | Layout keeps reading order and hierarchy across 4 widths | Structural diff score |
| `M-DIVERSITY` | Novelty vs parents | Perceptual distance from parents and siblings | Used for selection bonus, not composite |

Default performance budget: LCP under 2.0 s on mobile emulation, CLS under 0.05, first-load JS under 120 kB gzip.

## Judged functions

Judged functions cover what cannot be measured yet. Each uses a written rubric, a 1 to 5 scale, and 3 independent model judges. The score is the median, normalized to 0 to 1.

| ID | Question the rubric answers |
|---|---|
| `J-HIERARCHY` | Is it obvious what to look at first, second and third? |
| `J-CLARITY` | Can a first-time visitor say what this is and what to do within 5 seconds? |
| `J-BRAND` | Does it read as this brand and no other? |
| `J-CRAFT` | Alignment, optical balance, detail. Would a senior designer ship it? |

Rules:
- Judges see screenshots plus the target profile, never other variants' scores.
- Judge model names and versions are logged in the run record.
- Calibration: before a run, judges score 10 human-rated reference designs. A judge is used only if its rank correlation with human ratings is at least 0.6.
- Any judged function that repeatedly disagrees with Stage 6 human review is flagged for rubric revision.

## Field functions

| ID | Measures |
|---|---|
| `F-CONVERT` | Primary conversion rate vs baseline |
| `F-TASK` | Task success rate in moderated or unmoderated tests |
| `F-TIME` | Time on task |

Field results are reported separately and never mixed into the lab composite.

## Composite score

```
composite = gates_pass * sum(weight_i * score_i) / sum(weight_i)
```

- `gates_pass` is 1 if every gate passes, else 0.
- Weights live in `target.json` and are fixed before Stage 2. Changing weights mid-run starts a new run.

Default weights:

| Function | Weight |
|---|---|
| `M-TOKEN` | 1.0 |
| `M-RHYTHM` | 0.75 |
| `M-TYPE` | 0.75 |
| `M-TARGET` | 0.5 |
| `M-PERF` | 1.0 |
| `M-RESP` | 1.0 |
| `J-HIERARCHY` | 1.5 |
| `J-CLARITY` | 1.5 |
| `J-BRAND` | 1.0 |
| `J-CRAFT` | 1.0 |

## Adding a function

1. Propose it with ID, method, scoring and the human review reasons that motivated it.
2. Validate on at least one past run: does it separate accepted from rejected finalists?
3. Ship it in a minor version bump. Old runs keep their old version label.
