v3.0 — THE BRAIN · 2026-05-28
Every other "generative design" tool reaches for an API. ux-skill v3.0 doesn't. Same brief always produces the same output. Reproducible across machines. Yet the system genuinely learns from its own decisions log over time. Here's how the math holds.
An LLM in the generation path means:
uxskill synthesize call costs moneyv3.0 has none of this. uxskill synthesize finishes in single-digit milliseconds. No network. No API key required. Works offline on a plane.
The trick is in the layers:
Industry has a 7-axis seed dictionary. Tone tags push axes by ±0.10 to ±0.30. Forbidden tags clamp axes. All deterministic dict lookups + float adds. ~50 microseconds.
brief = Brief(industry="fintech-payments", tone=["bold", "serious"])
axes = compute_axes(brief)
# AxisValues(warmth=0.35, contrast=0.80, density=0.6,
# geometry=0.4, formality=0.95, motion=0.55,
# type_personality=0.4)
Compute 7-D Euclidean distance from the brief's axes to each brand's category-seed axes. Sort by distance. Tie-break by brand id alphabetically (deterministic). Top 8 win.
exemplars = pick_exemplars_by_axes(axes, n=8)
# [{"id": "stripe", ...}, {"id": "linear", ...}, {"id": "datadog", ...}, ...]
For each chosen exemplar, pull palette anchors, type stack, radius signal, spacing signal. Combine into a Vocabulary. Pure dict access. No NLP.
Weighted RGB mixing of palette anchors + warmth-shift = canvas / ink / primary hex codes. Modular scale at ratio 1.2/1.25/1.333 = size ladder. Spacing base picked from interactions.spacing_base_for(axes) (the documented conflict-resolution matrix). All deterministic.
If load_brands() returned brands in filesystem-order, two brands with identical axes would tie-break by inode. Different on different machines. v3.0 fixed this:
scored.sort(key=lambda t: (t[0], t[1])) # (distance, brand_id_alphabetical)
If compute_axes() hashed differently across Python versions, the axis values would drift. We use plain dict adds, no hashing.
If two brands had identical 7-D distance AND identical id (impossible — ids are unique), we'd still be deterministic because of the unique id constraint.
The decisions ledger. Every call appends one JSONL line to .ux/decisions.jsonl:
{"_v": 1, "ts": 1727380123.4, "command": "design",
"industry": "fintech-payments", "ui_type": "dashboard",
"picked_brand": "stripe", "picked_style": "swiss-grid",
"lint_score": 92, "user_accepted": true, ...}
The recommender, on the next call, reads this log filtered by (industry, ui_type) bucket. For each candidate, it counts how often that candidate's id appears in lines where lint_score >= 80 AND user_accepted = true. Each match adds +5 to the candidate's score before sorting.
Cold-start safe: fewer than 3 priors in a bucket = no-op. Behaves exactly like v2.0. Above the threshold, history bumps winning combinations to the top.
This isn't "ML" in the formal sense. There's no gradient descent. No labeled training data. No model checkpoint. But it IS learning, in the precise sense of "the system's output changes over time in response to feedback."
--force.pip install uxskill
uxskill stats --decisions # what your install has logged
uxskill stats --html # local dashboard at .ux/stats.html
uxskill lint . --score-only # just the int
uxskill evolve out.html # auto-loop to score 90+