第8章 §1

デプロイとSSOT

本節では、ai-workspace-standardsのL0→L1→L2→L3の4層構造、フォークモデルの設計理由、そしてファイル単位のSSOT(Single Source of Truth)の流れを扱う。原本は常に上から下へのみ流れ、逆方向にさかのぼる自動同期は存在しない。

この節で扱うこと
  • L0→L1→L2→L3の4層原理
  • フォークモデルと自動同期が不可能な理由
  • ファイル単位のSSOTの流れ

L0 → L1 → L2 → L3の階層構造(4層)

これまで登場したCONSTITUTION.md・templates/・variant・new-project.tsはすべて、一つの大きな原則の上で動いている。リポジトリはL0(ワークスペースルート)→ L1(共通テンプレート)→ L2(variantテンプレート)→ L3(稼働中のプロジェクト作業ディレクトリ)という4段階の階層(4-tier)として設計されており、信頼できる原本(Single Source of Truth)は常に上から下へのみ流れる。逆方向にさかのぼる自動同期は存在しない。前節で扱ったボイラープレートは、まさにこの4層のうちL1に相当する。このボイラープレートはL0で発行され、L2 variantが生まれる瞬間に一度だけ渡されるスナップショットである。

4つの層がそれぞれ担うもの

L0(ワークスペースルート)agents/pm.mdCLAUDE.mdGEMINI.mdscripts/*.tsといったファイルの唯一の原本である。編集はここでのみ行う。L1(templates/common/はL0から発行された共通インフラのスナップショットであり、新しいL2バリアント(variant)を作る際の出発点となるベースラインの役割のみを担う。L2は公式のvariantテンプレートであるtemplates/co-<name>/である — L1から分岐(フォーク)した後は独立して進化する。L3は、new-project.tsによってL2 variantからスキャフォールディングされた稼働中のプロジェクト作業ディレクトリであるProjects/<name>/である。開発者がエージェント(Claude Code、Antigravity CLIなど)を実行して日常的なコーディング作業を行う場所がこのL3であり、外部にクローンされるのではなく同じワークスペースリポジトリの中にそのまま存在する。L3プロジェクトはL2から派生するが、プロジェクトごとに独立して進化し、ローカルオーバーライドを持つことができる。

L0 — ワークスペースルート(C:\git\、唯一の原本)
  │  L0→L1: propagate-to-templates.ts(dev-syncのたびに自動・継続)
  ▼
L1 — templates/common/(共通テンプレートのスナップショット)
  │  L1→L2: create-l3-scaffold.ts(variant作成時点、一度だけ)
  ▼
L2 — templates/co-<name>/
  │  L2→L3: new-project.ts(プロジェクト作成時点、一度だけ)
  ▼
L3 — Projects/<name>/(稼働中のプロジェクト作業ディレクトリ)
     └─ L2をベースに生成、開発者がエージェントを日常的に実行する場所
     └─ フォーク後は独立して進化、自動逆同期なし
     └─ L2を公式テンプレートへ昇格するには明示的にl3-to-variant-pipeline.tsを実行
L0 ワークスペースルート 自動・継続 L1 templates/common/ スキャフォールディング時に1回 L2 — co-develop フォーク後は独立進化 L2 — co-work フォーク後は独立進化 L2 — co-game フォーク後は独立進化 ✕ 自動逆同期なし — L2→L1方向の矢印は存在しない

L1 → L2が「フォーク」で終わる理由

初期にはL1が変わるたびに、すでに作られたすべてのL2バリアント(variant)へ自動的に反映する方式を試みたが、3つの問題が繰り返し起きた。バリアント(variant)が意図的に異なる形へ修正しておいた部分を、L1の変更が静かに上書きしてしまうこと、新しいバリアント(variant)を追加するたびに伝播スクリプトにハードコードされたvariant一覧をいちいち手直ししなければならないこと、そして「意図した違い」と「同期を逃したことで生じた違い」を区別する方法がなかったことである。そのため現在はフォークモデル(Fork Model)を採用している。L1はバリアント(variant)を作るスキャフォールディングの時点で一度だけ内容を渡し、それ以降L2は完全に独立して進化する。L2の変更を公式テンプレートに反映するには、自動ではなくl3-to-variant-pipeline.tsを人が明示的に実行する必要がある(第11章でこの昇格手順を直接扱う)。一方、propagate-to-templates.ts --check-driftがL1と各L2の差分を定期的に報告することはあるが、この報告は読み取り専用であり、決して自動的に値を上書きすることはない。逆方向 — すでに作られたL3プロジェクトにL1の最新の変更を実際に反映する手順は、第10章 · L3プロジェクトのアップグレードで扱う。

ファイル単位のSSOT — 何がどこからどこへ流れるか

ファイル 原本(SSOT) 同期方式
CLAUDE.md / GEMINI.mdワークスペースルート手動伝播 + validate-templates.ts
agents/*.mdワークスペースルート agents/bun run agent:verify
AGENTS.mdワークスペースルートbun run agent:verify
variant.jsontemplates/co-<name>/ 自身当該バリアント(variant)自体が原本

agents/pm.mdは、L0→L1の関係を最もよく示す事例である。L0のagents/pm.mdが完全な原本であり、L1のtemplates/common/agents/pm.mdextends: ../../../agents/pm.mdというフロントマターでL0を参照しつつ、ワークスペース専用のセクション(例: Auto-Mode、ライフサイクル状態フィールド)を除いた「純粋な拡張」ファイルである。L2のtemplates/co-*/agents/pm.mdはL1を引き継ぐが、内容をそのままコピーするのではなく、variant固有のオーバーライドから必要な内容を最初から再生成する(「Layout Reconstruction」)。そのため、L0のagents/pm.mdが300行を超えても、L2のpm.mdは50〜100行程度に保たれる。

L0→L1→L2→L3を一文で表すと: 原本は上でのみ直す。下位の層は原本のスナップショットであるか、スキャフォールディング時点のコピーである。流れは常に上から下へのみ進む。 この原則に反する代表的な誤りが「新しいプロジェクト(L3)でエージェントファイルを直しておきながら、ワークスペースルート(L0)には反映しないこと」である — L3からL0へさかのぼる逆方向の流れはそもそも存在しないため、その修正はそのプロジェクト内でのみ有効であり、他の場所には永遠に反映されない。

ボイラープレート戦略 — 何を固定し、何を開放するか

第5章では、ボイラープレートを「プロジェクトごとに書き直す必要がないよう、あらかじめ用意しておいた出発点」と定義した。アーキテクチャの観点から見ると、この定義は直ちに実務的な問いへつながる — L1がL2に渡すもののうち、どこまでをプロジェクトが自由に修正できるようにするかという問いである。このリポジトリはこの問いに対して、ファイル単位で異なる答えを用意している。§5で扱うupgrade-project.tsの5分類(LOCKED・MERGE・PRESERVE・SYNC_IF_NEWER・PRUNE)は、実質的にこの戦略をコードとして固定したものである。

  • LOCKEDで固定するもの.githooks/*.gitattributes.gitleaks.toml) — 第3章のガードレールにおける最低限のセキュリティラインに相当するファイル群である。プロジェクトごとに異なる理由がなく、むしろ異なればセキュリティ事故につながるため、カスタマイズの余地をそもそも与えない。
  • MERGEで半分だけ開放するものCLAUDE.md/GEMINI.md.gitignoreagents/pm.md) — WORKSPACE-MANAGEDマーカーで囲まれた区間のみをL1が引き続き管理し、マーカーの外側はプロジェクトが自由に書く。「共通ルールは上から、プロジェクトの文脈は下から」を一つのファイルの中で同時に満たす折衷案である。
  • PRESERVEで完全に開放するものREADME.mddocs/context.mdsrc/) — L1はこれらのファイルの存在だけを確認し、内容には関与しない。プロジェクトの独自性を担う領域だからである。

新しいバリアント(variant)を設計する際(第6章・第11章)、この3つの範疇のうちどこに新しいファイルを配置するかをあらかじめ決めておけば、後になって「なぜこのファイルはアップグレードのたびに上書きされるのか」といった疑問がそもそも生じない。判断基準は単純である — セキュリティ・ガバナンスのように組織全体で必ず同一でなければならないものはLOCKED、組織のルールとプロジェクトの文脈が共存すべきものはMERGE、プロジェクト固有のものはPRESERVEに分類する。

モデルティア戦略 — 組織規模でティアを運用する方法

第5章では、tier: high/medium/lowが個々のエージェント役割にどう割り当てられるかを見た。ここでは、その割り当てを変更し維持していく流れをアーキテクチャの観点から見ていく。tierの値はagents/*.mdのフロントマターにあり、このファイルは上記のSSOT表で見たようにL0が原本であり、§5のupgrade-project.tsSYNC_IF_NEWERとして管理する — つまり、tier戦略そのものもL0→L1→L2の原則に忠実に従う。

  • 基本の割り当てはL0で一度定める。 Architect系はhigh、code-writer・docs-writer系はmedium、automation-engineer系はlowという原則をワークスペースルートのagents/*.mdで確定すれば、すべてのバリアント(variant)のL1テンプレートがこれを継承する。ただしPMだけは例外的な二重構造である — L0のagents/pm.mdはhighを維持し、L1/L2テンプレート(templates/common/agents/pm.md)はPR #623(2026-08-23)からmediumをデフォルトとしつつ、高度なオーケストレーションが必要な場合にhighへ上げられるescape hatchを備える。
  • ティア基準が変わればバージョンを上げる。「この役割はもうmediumではなくhighであるべきだ」といった組織レベルの再判断は、L0のagents/*.mdを直し@version(またはフロントマターのversion:)を上げる形で行われる。すでに作られたL3プロジェクトには自動的には反映されず、各プロジェクトがupgrade-project.tsを実行して初めて新しいティアを受け取る(第10章) — ティアの変更も例外なくフォークモデルに従う。
  • プロジェクトごとの例外はL2にのみ残す。 特定のバリアント(variant)の特定の役割だけ異なるティアが必要な場合(例: 特に複雑なドメインロジックを扱うcode-writerをmediumではなくhighにする)、そのプロジェクトのagents/*.mdをローカルで直接修正する。ただしこのファイルはSYNC_IF_NEWERの対象であるため、次回のアップグレード時にL1側のバージョンがより高ければ⚠️ CONFLICT警告とともに上書きされる可能性がある点を考慮する必要がある(§5の安全装置を参照)。
ボイラープレート戦略とティア戦略は、結局同じ問いに対する2つの答えである — 「組織全体で同一でなければならないものはL0/L1で強制し、プロジェクトごとに異なってよいものはL2に委ねる。」 両戦略には、その境界をファイル単位(LOCKED/MERGE/PRESERVE)とフィールド単位(tier)でそれぞれ明示的に引いているという共通点がある。