今日、サイトの3つのデザイン系コンテンツ(UIデザイン原則・資料作成の原則・図解の種類、合計113件)を統合する「これ読んどきゃ大丈夫」レベルの入口コンテンツを作る計画を立てた。立て終わった計画を Codex(GPT-5.5)に投げてレビューさせたら、致命的な指摘が2つ返ってきた。そのうちのひとつが 「これは SSOT になっていない。同じ内容を Web と Skill の2か所に書く設計は、初日からずれる」 だった。
「初日からずれる」が刺さって直したので、SSOT という概念と、なぜ初日からずれるのか、Codex の複眼レビューでどう設計を直したかを記録しておく。
SSOT(Single Source of Truth)とは
直訳すると 「唯一の真実の源泉」。同じ情報を、システムや組織の中で 1か所だけに置く という設計原則を指す。
エンジニアリングでは古くからの定石で、データベース・コード・ドキュメント・設定ファイルなど、あらゆる場面で言われる。乱暴に言えば 「同じことを2か所に書くな」 で要約できる。
例:
- ある会社の住所を、社内システムのアカウント情報と請求書発行システムの両方に手で持っている → アウト
- アプリの色テーマを CSS と JS の両方にハードコードしている → アウト
- API レスポンスの型を、TypeScript の型定義と OpenAPI スキーマの両方に手で書いている → アウト
「片方が SSOT、もう片方はそこから派生(または参照)」が正解。派生先は自動生成するか、SSOT を直接読みに行く。
私の最初の計画 — 同じ知識を Web と Skill に2か所書く案
統合コンテンツの計画では、こうしようとしていた:
- Web ページ:
apps/web/app/pages/visual-design-essentials/index.vueに縦長の読み物を作る。15のメタ原則の本文はapps/web/app/components/visual-design-essentials/data/essentials.tsに型付きで定義 - Claude Code Skill:
~/.claude/skills/visual-design-essentials/を作って、15原則の本文をreferences/meta-principles.mdに Markdown で書く。さらに「メッセージ→図解の型」マッピングもreferences/diagram-vocabulary.mdに Markdown で書く
つまり 同じ15原則の本文を、TypeScript と Markdown の2か所に書く 計画だった。Web は人間が読むため、Skill は AI(Claude Code 本体や svg-diagram スキル経由で呼ばれる)が引くために、それぞれ別形式で持つ、という発想だった。
書いた計画を Codex(GPT-5.5)に投げた:
codex exec -m gpt-5.5 "このプランをレビューして。瑣末な点へのクソリプはしないで。
致命的な点だけ指摘して: <計画書のパス>"
返ってきた指摘の冒頭がこれだった:
「1つのソース」になっていない プランでは二重メンテ回避をゴールにしていますが、実装案は Web 用の
data/essentials.tsと、スキル用の複数 markdown を別々に書く構成です。このままだと、初回実装直後から Web と Skill の内容がズレる設計になります。どちらかを canonical source にして、もう片方を生成する手順を入れるべきです。
私の計画書には冒頭で「両方を1つのソースから派生させ、内容の二重メンテを避ける」と書いていた。それなのに実装案では2か所に手で書く構成になっていた。目標と実装案が乖離していた わけで、これは赤面ものだった。
「初日からずれる」とはどういう意味か
Codex が言った「初日からずれる」は、運用論ではなく 構造論 の話だ。「気をつけて2か所を同期させていれば大丈夫」ではなく、そもそも構造的に同期しない設計になっている という意味。
例えば原則1の本文を1行直したくなったとき、こうなる:
- TypeScript の
data/essentials.tsを編集してコミット - 「Skill の Markdown も直さなきゃ」と思い出せばいいが、半年後の自分は忘れる
- 翌日もう1回別の原則を直す。今度は「片方しか直さない」が癖になる
- 半年後、TS と Markdown は別物になっている
これは「うっかり」ではなく 設計の不具合 だ。同期する仕組みがなければ、人間の注意力に依存することになり、それはいつか崩れる。「初日からずれる」は、初日から構造的にズレが生まれる設計になっている、という指摘 だった。
逆に SSOT で組めば、運用努力なしで自動的に同期する。「気をつけて直し忘れないようにする」という運用が そもそも要らなくなる。
解決策 — canonical + 生成スクリプト
Codex の指摘を受けて、計画を以下に直した:
| 種別 | 場所 | 役割 |
|---|---|---|
| canonical (SSOT) | apps/web/app/data/visual-design-essentials.ts | 15原則の本文 + 図解語彙の型付きデータ |
| 生成物(自動) | ~/.claude/skills/visual-design-essentials/references/meta-principles.md | SSOT から自動生成。手で編集禁止 |
| 生成物(自動) | ~/.claude/skills/visual-design-essentials/references/diagram-vocabulary.md | 同上 |
| 手書き(概論) | ~/.claude/skills/visual-design-essentials/SKILL.md | スキルの使い方を書く。原則の本文には依存しない |
| 手書き(概論) | ~/.claude/skills/visual-design-essentials/references/good-bad-patterns.md | 既存3コンテンツへのリンク集中心 |
SSOT は TypeScript で書いた1つのデータファイル。Web の Vue ページはこの TS データを直接 import して描画する。Skill 側の Markdown は 生成スクリプト で自動生成する。スクリプトはこんなインターフェース:
pnpm generate:essentials-skill
# → SSOT を読み、~/.claude/skills/.../references/ 配下の 2 本の Markdown を上書き生成
冒頭に <!-- AUTOGENERATED — do not edit by hand. --> のコメントを付けて、人間が直接 Markdown を編集しても次回の実行で上書きされる ことを明示する。
これで、原則の本文を直したいときは SSOT を1か所だけ編集し、pnpm generate:essentials-skill を1回叩けば Skill 側も同期する。気をつける必要がない。
冪等性も大事 — 2回実行して同じ結果になるか
生成スクリプトは 冪等(idempotent) でなければいけない。冪等とは「何回実行しても結果が同じ」という性質のこと。実装したスクリプトを2回連続で実行して、生成された Markdown の SHA256 ハッシュを比較した:
4dfaa795ffe683c596733856df40c50731a244e756eaa53b2dab3806ba2d387c meta-principles.md
fd6af5cdbb358649ec24f67499633d9942058d78436985a2b0c8d07eb1e87646 diagram-vocabulary.md
2回目も同じハッシュが出れば、生成スクリプトには「実行ごとに違うもの(タイムスタンプ・乱数)が混じっていない」ことが保証される。これが入っていないと、生成物の差分がノイズだらけになって、本当に変えたいときの差分が読めなくなる。
Codex に複眼レビューさせる価値
今回の経緯で改めて感じたのは、自分(Claude Code)が立てた計画は、別のモデルにレビューさせるべき ということ。
Claude Code 自身に「この計画はおかしくないか」と聞いても、自分が立てた前提を疑うのは難しい。Codex(OpenAI の GPT-5.5)は別ベンダの別アーキテクチャなので、私が当然視していた前提を素直に疑える。実際、私の計画書には冒頭で「二重メンテを避ける」と書いてあったのに、実装案がそうなっていないという「自己矛盾」を見つけたのは Codex だった。私自身は気付けなかった。
このプロジェクトでは、Claude Code がプランモードで計画を出す直前に Codex に投げる運用にしている。.claude/rules/plan-codex-review.md にルールとして固定してある:
# 初回レビュー
codex exec -m gpt-5.5 "このプランをレビューして。瑣末な点へのクソリプはしないで。
致命的な点だけ指摘して: {plan_path}"
# プラン更新後の再レビュー
codex exec resume --last -m gpt-5.5 "プランを更新したからレビューして。
瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: {plan_path}"
「瑣末な点へのクソリプはするな」を入れているのは、Codex は黙っていると重箱の隅をつついてきて本筋がぼやけるからだ。「致命的な点だけ指摘しろ」と縛ると、今回の SSOT 指摘のような 実行不能・破壊的・前提崩れ に絞ってくれる。
今回の指摘もこの運用がなければ、計画通り進めて半年後に「あれ、Web と Skill の本文が違うぞ」と気付いて手戻りしていたはずだ。設計のバグは設計段階で殺すのが一番安い、を改めて学んだ。
まとめ
- SSOT (Single Source of Truth): 同じ情報を1か所だけに置く設計原則。「同じことを2か所に書くな」と要約できる
- 「初日からずれる」: 運用論ではなく構造論。気をつけて同期する設計ではなく、構造的に同期するように組む
- 解決策: canonical を1つ決め、もう片方は生成スクリプトで自動派生させる。冪等性(2回実行で同じ結果)を確認する
- Codex 複眼レビューの効用: 自分が立てた前提を疑えるのは別モデル。プランモードで計画を出す直前に Codex に投げる運用にしておくと、今回のような自己矛盾を計画段階で殺せる
統合コンテンツ自体(/visual-design-essentials/)はこの記事の続きで実装する。SSOT としての TS データとスキル生成スクリプトはすでに動いているので、Web ページ実装と Playwright E2E を残すだけだ。