ux-skill v3.0 — The Brain をリリース。ブランド仕様はもうテンプレートではなく、訓練データです。
v2 はカタログから選んでいた。v3 はそこから蒸留する。同じ 160 のブランド仕様、まったく違う役割です。これまでのリコメンダーは 1,182 件のエントリを採点して最も近い候補を返していました。新しいシンセサイザーはそれらを語彙として読み、ブリーフから新しいデザイン言語をコンパイルします。呼び出すたびに、その呼び出し以前には存在しなかったシステムが返ってきます。
テンプレートではない。
一段落でいう、今回のシフト
v2 はリコメンダーでした。ブリーフを渡せば、整備済みのカタログ — 84 のスタイル、176 のパレット、160 のブランド仕様、70 のタイプペア — から最も近い候補を返す。出力は常にデータベースの 1 行でした。v3 はカタログをすべて維持したまま、その役割を反転させます。1,182 件のエントリは、いまやエンジンが蒸留する語彙です。ブリーフはもう 1 行を引き当てるのではなく、7 つの軸の値にコンパイルされ、その値が新しいトークンを合成する。カタログは「軽やかで企業的なもの」がどんな形をしているかをエンジンに教え、エンジンはこれまで世に出たことのない「軽やかで企業的」な新しいシステムを生成します。データは同じ。役割はまったく違います。
v2 で壊れていて、v3 が直すもの
実際のバグが 3 つ、設計上の穴が 1 つあります。どれも騒がしいバグではなく、もっともらしい出力が返ってきて、私たちもそのまま出荷していた静かな種類のものです。アップグレード仕様を書こうとして外部レビューに正面から向き合わされて、ようやく気付きました。
1. ファイルシステム順のタイブレークによる擬似的な決定論
リコメンダー内部のいくつかのソートは、類似度で候補を採点し、同点は Python の os.listdir が返す順序で割っていました。macOS ではアルファベット順、Linux ではファイルシステムジャーナルへの挿入順。新規クローンと長く使ったクローンでは、タイブレークの結果がひっくり返り得ました。私たちはエンジンを決定論的だと言っていました。同点までは決定論的だったのです。同点になった瞬間からは、OS が偶然憶えていた順序に従っていました。
v3 はすべてのソートに、ブランド ID のアルファベット順という明示的なタイブレークを焼き付けました。同じブリーフ、同じ軸、同じ出力 — マシン、ファイルシステム、Python バージョンを越えて。シンセサイザーを 3 つの一時ディレクトリ上で 200 回回し、バイトレベルで出力が一致することを断言するテストが入っています。
2. 偶然で解決していた軸の衝突
v2 では、ブリーフが反対方向に引く 2 つのタグ — たとえば dense と corporate — を持っていたとき、リコメンダーのスコアラーはどこかの隠れた係数で片方を重く扱い、出力はその係数が偶然有利にしたタグへ寄っていました。文書化されたルールはなく、結果を後から説明することはできても、事前に予測することはできませんでした。
v3 はこれを軸の相互作用マトリックスに持ち上げました。文書化されたすべての衝突に、明示的な解決と理由が付いています。density と formality は spacing をめぐって争います — 密なブリーフでは density が勝ち(Bloomberg 流)、軽やかなブリーフでは formality が勝ちます(ラグジュアリー)。マトリックスはテスト済みで、読めて、コミットされています。
3. エバリュエーターはシンセサイザー自身の宿題を採点していた
いちばん恥ずかしいのはこれで、気付けたのは ChatGPT が v2.1 仕様の外部レビューで指摘したからです。彼らが自己参照ドリフト (self-referential drift) と呼んだ問題でした。フローは綺麗に見えました。シンセサイザーがブリーフからシステムをコンパイルし、続いて score_tone_match がそのシステムとブリーフのトーンの一致度を評価する。問題は、score_tone_match がシンセサイザーと同じロジックでブリーフから軸を導き、それからシンセサイザーの軸を自分が導いた軸と比較していたことです。比較の両側が同じ関数を通っていました。当然スコアは良かった。シンセサイザーは自分のルーブリックで自分自身を採点していたのです。
v3 はこれを切り離します。score_tone_match はいま、シンセサイザーの軸ロジックに一切触れない独立したマッピングを通して、合成出力とブリーフの生のトーンタグ — warm、bold、minimal といった文字列 — を直接比較します。両者がもう互いをカンニングできなくなりました。この指摘は外部レビューに負っており、ここに記録として書いておきます。
4. 決定ログは書き捨て — 一度も読まれなかった
v2 はリコメンドのたびに .ux/decisions.jsonl へ 1 行書いていましたが、読み返すものは何もありませんでした。私たち自身のデバッグ用のログファイルで、フィードバック信号ではなかったのです。リコメンダーは 1 回目も 1,000 回目も同じように振る舞いました。学習は存在せず、リポジトリには記憶があるのにエンジンはそれを無視していました。
v3 はそのループを閉じます。台帳にはロックされたスキーマ (_v: 1) があり、リコメンダーは同じ (industry, ui_type) バケットの過去の勝者で候補を再ランクし、十分にスコアが高くかつユーザーが実際に出荷した決定だけが再ランクに数えられます。脳はようやく自分の履歴に目を持ちました。全体は第 6 節で歩きます。
7 軸のシンセサイザー
v3 の中心は、ブリーフを 7 つの数値軸にマッピングし、その 7 軸を完全なデザインシステムにマッピングする決定論的な関数です。軸は魔法ではありません — それぞれが文書化されたレンジを持つ正規化スカラーで、特定のトークン束を指しています。カタログから 1 行選ぶのではなく軸の上で動かす意味は、カタログ内の実在ブランドが使っていない組み合わせも含めて、空間内の任意の組み合わせに到達できることです。カタログは空間の形を定義し、シンセサイザーはその内部のどこへでも着地できます。
| 軸 | 何を測るか | マッピング先 |
|---|---|---|
| warmth | パレットの色温度 — クールなチャコールから温かなシエンナまで | パレットの色相系、アクセントの彩度カーブ |
| contrast | 視覚的な強弱 — quiet / balanced / loud | タイプスケール比 (1.200 / 1.250 / 1.333)、影の深さ、アクセントの強さ |
| density | 1 ビューポートあたりの情報密度 — airy から dense まで | スペーシングスケールのベース (4/8/12/16px)、行間の倍率 |
| geometry | エッジの人格 — sharp / balanced / soft | radius スケール (2/8/18px)、角のトポロジー、ボーダーの太さ |
| formality | トーンのレジスタ — playful / balanced / corporate | タイプ重量カーブ、トラッキング、caption から hero までの幅 |
| motion | アニメーションの予算 — restrained / balanced / cinematic | デュレーションのランプ (120/240/420ms)、イージング系、スクロール挙動 |
| type_personality | 書体の声 — humanist / neutral / geometric / expressive | ディスプレイ書体ファミリー、ウェイトのはしご、イタリック戦略 |
Python のエントリーポイントは、ひとつのサンプルに収まる程度に小さいです。ブリーフを渡せば、軸の値、選ばれたパレット、タイプ設定、スペーシングスケール、radius スケール、モーションプリセットが付随した合成済みシステムが返ってきます。
# ブリーフからシステムへ、1 回の呼び出しで from engine.synthesizer import synthesize brief = Brief( industry="fintech-payments", tone=["bold", "serious"], audience=["merchants", "developers"], ) sys = synthesize(brief) # sys.mode == "pure_synthesis" # sys.axes == {warmth: 0.32, contrast: 0.72, density: 0.58, ...} # sys.palette, sys.type, sys.spacing, sys.radius, sys.motion
同じブリーフが入れば、同じ軸が、同じトークンが出ます。常に。シンセサイザーには乱数シードがありません — シードする乱数がないからです。
3 つのモード、1 つの判定ロジック
シンセサイザーはブリーフの中身に基づいて 3 つのモードを自動振り分けします。ほとんどの用途で、モードを手で選ぶフラグはありません — ブリーフの形が決めます。ロジックは意図的にシンプルで、リファレンスブランドと strict フラグの有無が経路を決定します。
100% 指定ブランド
reference_brands=[stripe] に strict=True を付けると、Stripe の仕様がそのまま返ります。解釈もミキシングも合成もなし。ブランドが既に存在し、仕様が権威で、ブリーフはトークンだけが欲しい — そんな場面のための経路です。Figma ファイルと文書化されたシステムがあり、即興しないコードが必要なときに選ぶ道です。
ブランドに錨を下ろし、ブリーフに合わせて軸で調整
reference_brands=[stripe] を strict=True なしで指定すると、70% の Stripe トークンと、軸の近さで選んだ 4 つの兄弟ブランドから 30% の軸由来デルタが融合されます。出力は紛れもなく Stripe のままで、ブリーフのトーンへ少しだけ曲がります — コンシューマー機能向けにはより遊びのある Stripe、エンタープライズダッシュボード向けにはよりフォーマルな Stripe。錨となるブランドはあらゆる衝突を生き残ります。
ブランド未指定 — 呼び出しごとに新しい言語
ブリーフに reference_brands がない場合、シンセサイザーはブリーフから 7 軸を採点し、カタログの中から軸距離で最も近い 8 つの実例を見つけ、それらの共通構造を蒸留し、新鮮なパレット + タイプ + スペーシング + radius + モーション一式を出します。異なるブリーフは同じ空間の異なる領域に着地し、すべての出力は内部的に一貫していて、自分自身として識別可能です。
CLI からは 3 つの経路はこう見えます:
# strict — 100% そのブランド $ uxskill synthesize --brand stripe --strict # anchor — 70/30 $ uxskill synthesize --brand stripe # pure synthesis — ブランドなし $ uxskill synthesize --industry fintech-payments --tone bold
1 つのシンセサイザー、1 つの判定ロジック、3 つの出力の人格。ユーザーは実装を切り替えません — ブリーフ自身が経路を選びます。
軸の相互作用マトリックス
軸は同じトークンを引っ張り合います。density はタイトな間隔を望み、formality はラグジュアリーな佇まいのために寛大な間隔を求める。geometry は編集的な切れ味のために鋭角を欲し、formality は金融ダッシュボードのために柔らかな角を求める。v2 ではどちらの係数が偶然大きいかで決まっていました。v3 では文書化された衝突それぞれに、宣言された結果と、それが指し示すデザイン流派があります — 名前が付いているので、読んで反対することもできます。
代表的な 4 ケース、いずれもテストフィクスチャとしてコミット済みです:
density が勝つ。Bloomberg 流の答えです。密なデータダッシュボードを企業的なレジスタで求めるブリーフでは、情報密度が最高の徳 — formality は密度における可読性に頭を下げます。
formality が勝つ。ラグジュアリー金融の答えです。airy + corporate はプレミアムバンキング、プライベートウェルス、エグゼクティブダッシュボードのブリーフ — 要素間の空間そのものがシリアスさのメッセージです。
geometry が勝つ。NYT 流の答えです。sharp + corporate は編集的なブリーフ — 直角と 2px の極小 radius は、組織的で、考え抜かれた、ブロードシート紙の読みになります。
両軸が揃います。Glossier 流の答えです。柔らかなジオメトリと遊び心のあるレジスタは、寛大な丸めへ収束します — 18px は矩形が小石として読まれ始める閾値です。
なぜこれが重要か。v2 では、同じブリーフがその月にスコアラーで勝った係数次第で 4px にも 12px にも着地し得ました。v3 では dense + corporate は常に 4px、airy + corporate は常に 12px です。ルールは読めて、テスト可能で、議論可能です。ある解決に同意できないなら、マトリックスに対して issue を立てればいい — 議論は趣味の話になり、隠れた定数の話ではなくなります。
脳が学習する
ここがプロジェクトにとって本当に新しい部分です — エンジンはあなたのローカルの決定から学習します。仕組みは小さく、決定論的で、完全にオフラインです。テレメトリも、アカウントも、クラウド同期もありません。あなたのインストールはあなたのインストールから学ぶ。リポジトリが違えば、違う脳が育ちます。
台帳
すべての /ux-recommend と /ux-synthesize の呼び出しは、.ux/decisions.jsonl に 1 行を書き込みます。スキーマは _v: 1 でロックされています:
{
"_v": 1,
"ts": "2026-05-28T14:22:09Z",
"frame": { "industry": "fintech-payments", "ui_type": "dashboard", ... },
"system": { "style_id": "editorial-calm-dark", "palette_id": "charcoal-amber", ... },
"axes": { "warmth": 0.31, "contrast": 0.72, ... },
"lint_score": 88,
"user_accepted": true
}
再ランク
次の呼び出しでは、リコメンダーは過去のエントリを (industry, ui_type) でグループ化します。各候補は、同じバケットの先行例にマッチすれば +5 のブーストを受けます。lint_score >= 80 かつ user_accepted = true の先行例だけが算入されます — 拒否された出力からは学びません。バケットには、いかなる再ランクも発動する前に、最低 3 件の有効な先行例が必要です。それ以下なら、エンジンはコールドスタートで動き、新規インストールと同じ振る舞いをします。
保証
ここで明言できる保証は狭く、声に出して言っておく価値があります。決定論は保たれます。同じブリーフ + 同じ台帳は、常に同じ出力を返します。+5 のブーストはタイブレークの前に適用され、ブランド ID のアルファベット順という規則はその下層でなお勝ちます。コールドスタートは安全です。新しいリポジトリは、他のすべての新しいリポジトリと同じ振る舞いをします。学習はローカルです。あなたの決定はマシンを離れません。
あなたのインストールが何を学んだかは、いつでも確認できます:
$ uxskill stats --html [OK] Wrote .ux/stats.html [OK] Open in browser to see your install's learned priors
HTML ダッシュボードは、バケットごとの決定件数、平均 lint スコア、最頻パレット、軸の分布を表示します。これが自己学習の目に見える証拠です — あなたが出荷を続けるにつれ、インストールの趣味プロファイルが時間とともに濃くなっていくのが見えます。何ひとつサーバー上にはありません。あなたのリポジトリの中、平文の JSONL と生成された HTML ビューにあります。
/ux-evolve オートループ
v3 の新しいコマンドが /ux-evolve です。これまでポリッシュループは手作業でした — lint を実行し、findings を読み、直して、もう一度 lint。Evolve はそのループを閉じます。アーティファクトと目標スコアを渡せば、lint を走らせ、6 回の冪等なポリッシュパスを適用し、再 lint し、目標で止まるか、プラトーで止まるか、5 ラウンドの上限で止まります。
1 ラウンドの形:
- Lint — A11y、コンテンツ、品質、タイポグラフィにまたがる 145 の決定論的正規表現ルール。0 から 100 までのスコアと、findings のリストを返します。
- Polish — 重大度の高い findings から先に直す 6 つのパス。冪等です: ポリッシュ済みの出力に再適用しても no-op になります。
- Re-lint — ポリッシュされたアーティファクトを採点します。
- Decide — 新しいスコアが 90 以上なら止まる。前ラウンドからのデルタが 5 未満(プラトー)なら止まる。5 ラウンド目なら止まる。それ以外はループ。
品質ゲートは 65 で硬く固定されています。最終スコアが 65 を下回ったら、エンジンは --force なしではアーティファクトの出荷を拒否します。理由はこうです: 65 以下の出力は AI の slop と認識できるレベルで、自分のエンジンが静かに slop を吐くのを許せないからです。ユーザーはゲートをオーバーライドできますが、オーバーライドは明示的で、決定台帳にも記録されます。
具体例: あるフィンテックダッシュボードのスキャフォルドが最初の lint で 72 点 — ほとんどはフォーカスステートの欠如、いくつかの低コントラストラベル、ディスプレイサイズの Inter という finding。ラウンド 1 でフォーカスステートをポリッシュして 81 点。ラウンド 2 でコントラストを直して 86 点。ラウンド 3 で Inter をブリーフ本来のディスプレイ書体に差し替え、caption サイズをリバランスして — 91 点、目標到達、ループ終了。3 ラウンド、拒否なし、出荷。
作らなかったもの(と、その理由)
v2.1 のマキシマリスト仕様にはあったが、v3 には入れなかったものがいくつかあります。どれを、なぜ入れなかったかを言うことが、スコープについて正直に話す唯一の方法です。
LLM をループに入れない
マキシマリスト案には LLM 判定の主観的美学軸がありました — 「このシステムは編集的に感じるかモデルに聞く」。原則として却下しました。エンジンの全要点は決定論です: 同じブリーフ、同じ出力、明朝出社して驚くことなし。シンセサイザーに LLM が入ると、すべての呼び出しが再現不可能になります。v3 は LLM を 0 回呼びます。シンセサイザーは JSON 上の純粋な Python、リンターは正規表現、エバリュエーターはルールベースです。
複数候補の遺伝的変異なし
案には遺伝的アルゴリズムのステップもありました — 呼び出しごとに N 個の候補を出し、変異させ、採点し、最適個体を返す。ブランチで試しました。出力は悪くなりました。変異がノイズを入れ、ポリッシュループがそれを取り除く羽目になったからです。v3 は単一アーティファクトの合成と、ポリッシュループを採用しています。候補は 1 つ、冪等なパスが 6 つ。結果も上、コードも小さい。
コマンドのリネームなし
案は /ux-recommend を /generate:ui に、/ux-polish を /mutate:ui にリネームしていました。私たちは既存の 22 コマンドをすべて元の名前で残し、/ux-evolve を 23 番目として追加しました。v2 から v3 へのインストールが、誰の筋肉記憶もシェルエイリアスも壊さないようにしたかったからです。
「無限の空間のためにカタログを焼く」もなし
案の最大主義的バージョンは、カタログは負債だ — シンセサイザーは自力でデザイン空間全体に到達できるから、1,182 件のエントリは捨てるべきだ — と言っていました。私たちはすべてのエントリを残しました。カタログこそが、シンセサイザーに空間の形を教えるものです — 実例がなければ軸に錨はなく、出力は漂い始めます。v3 はカタログを語彙として読み、v3 はその語彙を必要としています。
これらは趣味の判断です。優先順位が違えば反対する人もいる、それで構わない。やらなかったこと、その理由を記録に残すのがプロジェクトに対する義理です。
v3 の数字
既存の 22 スラッシュコマンドは元の名前のまま、23 番目として /ux-evolve。15 の MCP ツールはそのまま、3 つの新規 — ux_synthesize、ux_decisions_query、ux_decisions_stats — を追加し、シンセサイザーを駆動したり、脳が学んだものを照会したりしたいエージェントに提供します。カタログとリンターは触っていません。17 IDE のインストーラーも触っていません。17 の現地化されたホームページと README も変更なし。v3 は表層では足し算、コアでは書き直しです。
v2 はカタログから選ぶリコメンダーでした。v3 はそこから蒸留するコンパイラです。同じデータが、逆の仕事をしています。
60 秒でインストール
v3 は v2 と同じ 3 経路 — pip、npm、IDE プラグインマーケットプレイス — で出荷されます。init ステップは IDE を自動検出し、適切な設定ファイルを正しい場所に書き出します。synthesize ステップはブリーフ、もしくは industry + tone のペアを取り、完全なデザインシステムを現在のリポジトリの .ux/last-system.json に返します。
$ pip install uxskill $ uxskill init # IDE を自動検出 $ uxskill synthesize --industry fintech-payments --tone bold [OK] Mode: pure_synthesis [OK] Axes: warmth=0.31, contrast=0.74, density=0.58, geometry=0.42, ... [OK] Wrote .ux/last-system.json (palette, type, spacing, radius, motion) [OK] Wrote .ux/decisions.jsonl (1 entry, schema _v: 1)
サポート対象のすべての IDE で同じインストールパス: Claude Code、Cursor、Windsurf、GitHub Copilot、Gemini CLI、Codex、Kiro、Cline、Continue、Aider、Zed、JetBrains AI、Pieces、Tabby、Tabnine、CodeWhisperer、Roo Cline。MCP stdio サーバーがすべての真実の源です。
v3 は基盤であって、完成された終点ではありません。
7 軸シンセサイザーは私たちが証拠を公開している仕事です。8 番目と 9 番目の軸 (saturation_strategy、surface_depth) はスケッチ済みで未出荷 — dark / light の分割やグラデーション戦略を深めるものになります。決定台帳は今日はリコメンダーの再ランクだけが消費しています。エバリュエーターと lint スコアラーが台帳を読み始めるのは v3.1 を見込んでいます。私たちが却下したマキシマリスト案も消えていません — いくつかは、後で、それが効くことを証明できたら入ってくるでしょう。
もし出力が満足できないブリーフを見つけたら、もしくはマトリックスの解決があなたの趣味と合わないなら、issue を立ててください。マトリックスはコミットされ、テストはコミットされ、脳はあなたのリポジトリにあります。議論は、これからは読める基盤の上で行われます。
あなたにとって、何が変わるか
v2 にいた人へ: 何も壊れません。すでに使っている 22 コマンドはこれまでどおりに動きます。lint は同じ速さで走ります。MCP サーバーは 18 のうち 15 のツールを元の名前で公開し、3 つの新ツールを追加します。アップグレードしても /ux-synthesize や /ux-evolve を呼ばないなら、毎日のワークフローはまったく動きません。
エンジンがレコメンドするのではなく生成するのを待っていた人へ: それが v3 です。シンセサイザーが新しい中心。/ux-recommend は今も動き、台帳から再ランクします。/ux-evolve はポリッシュループを閉じます。ブランド仕様は訓練データであり、テンプレートではありません。同じ 1,182 件のエントリが、まったく違う仕事をしています。
v3 を The Brain と名付けたのは、それが作り上げられたものだからです — 閉じたフィードバックループを持つ、制約付き生成デザインコンパイラ。完全にオフライン。LLM は 0 回呼ぶ。同じブリーフ、同じ軸、同じ出力。違うブリーフ、違う領域、無限の出力。あなたのインストールはあなたのリポジトリから学ぶ。違うリポジトリは違う脳を作る。すべては MIT で、普通のマシンで sub-300ms、pip でインストールできます。
気になっているブリーフで走らせてみてください。返ってきたものを見てください。出力があなたの趣味と合わないなら issue を立ててください — 衝突はマトリックスに入り、ルールは名付けられ、次のインストールはより良く振る舞います。それが脳の複利のかかり方です。