spec 1.0 · 規範
Leji 仕様(1 ページ)
規範となる仕様の参考訳全文を、読む順に 1 ページへまとめています。通読にも検索にも利用できます。各セクションから、見出しごとに引用用アンカーを備えた個別ページへ移動できます。
Leji 仕様
Leji は、AI ネイティブなチームの共有コンテキストレイヤーを定めるオープンな仕様です。人と AI エージェントが作業のたびに読む、リポジトリ所有のコンテキストについて、チームによる保存、統制、読み込み、保守の方法を規定します。
| 仕様バージョン | 1.0.0 |
| ステータス | GA。v1.3.0 のリファレンスツールのリリースをもって凍結。互換性を壊す変更には新しいメジャーバージョンが必要です。 |
| 編者 | Vuong Nguyen |
| 1 ページ版 | 仕様の全文を 1 ページで |
原則(非規範的)#
- 指示ではなく意図を。 Leji が捉えるのは、命令形で記述されたベンダーごとの指示ではなく、持続する意図(何を意味し、何が成立していなければならず、なぜそうなのか)です。人もエージェントも、宣言された意図とタスクの文脈から行動を導き出します。
- 階層ではなく輪。人から人へ、人から AI へ、人から AI を経て人へ。この 3 つはいずれも、ひとつの共有コンテキストレイヤーを中心とする主要な流れです。アクセスは平等でも、権限は平等ではありません。コンテキストレイヤーにアクセスできる者は全員がその全体を読み、誰でも提案できますが、承認するのは人です。参加はツールではなくロールに基づきます。git に直接触れない参加者も、この輪では対等な存在です。アクセス自体を付与するのはバージョン管理システムであり、Leji ではありません。輪の範囲は、そのコンテキストレイヤーの読み手に限られます。
- 善意ではなく仕組みを。共有コンテキストは、放置すれば劣化します。現実は変わっても文書は自動では変わらず、ウィキを最新に保つ仕組みもありません。Leji で強制力を持つのは善意ではなく、機械的な仕組みです。変更はコードと同じレビューのゲートを通り、機械的なずれがあればツールのチェックは失敗し、鮮度の期限によって古くなったものが識別されます。古くなったコンテキストが黙って現行のものとして扱われることはありません(規範については governance.md → 鮮度)。
この仕様の残りは、これら 3 つの原則から規範的に導かれるものです。
適合性の言葉づかい#
この仕様における MUST、MUST NOT、REQUIRED、SHOULD、SHOULD NOT、RECOMMENDED、MAY、OPTIONAL の各語は、RFC 2119 に記載されたとおりに解釈されます。
この仕様の引用(非規範的)#
セクションを引用する際は、見出し、仕様バージョン、セクションアンカーへのパーマリンクを記載してください。仕様サイトでは、どの見出しにもホバーするとアンカーが表示されます。
- 形式: Leji 1.0, §セクション:
https://leji.org/spec/<document>/#<anchor> - 例: Leji 1.0, §The circle, normatively:
https://leji.org/spec/governance/#the-circle-normatively
バージョン(Leji 1.0)は必ず示してください。互換性を壊す変更は新しいメジャーバージョンとして公開されるため、バージョンを固定した引用なら、仕様が更新された後も正確さが保たれます。
用語#
以下の語は、すべての規範的文書を通じて一貫した意味で使われます。
| 用語 | 意味 |
|---|---|
| context layer(コンテキストレイヤー) | この仕様が統制する成果物。リポジトリが所有し、バージョン管理された、人が読める文書と機械可読な成果物の集合であり、チームの持続する運用上のコンテキストを表します。曖昧さを避ける完全形は「Leji コンテキストレイヤー」です。常に「コンテキストレイヤー」と書いてください。単なる「レイヤー」は、フェデレーションで数えられる個体(兄弟、ホスト、マウントされた、制限された、コンパニオン、到達不能なコンテキストレイヤー)を指すときに限ります。 |
| agent(エージェント) | 行為する AI システム。リポジトリのコンテキストを読み込み、作業を行うか支援し、変更を提案することがあります。規範における行為者を指す名詞です。 |
| person / people(人) | 人間の参加者。承認の権限を持つのは人です。 |
| participant(参加者) | 人またはエージェント。 |
| audience(読み手) | リポジトリの権限、およびチェックアウトを見せているファイルシステムや共有ドライブの権限によって、そのコンテキストレイヤーを読むことを許された人とエージェント。「全員が読む」の範囲はそのコンテキストレイヤーの読み手に限られます。読み手が異なる場合は、ひとつのレイヤーの中で内容を出し分けるのではなく、別のコンテキストレイヤーで応じます。 |
| agent host(エージェントホスト) | エージェントがそれを通じて動作する製品やランタイム(たとえば Claude Code、Codex、Cursor)。ベンダーアダプタが設定するのはエージェントホストです。 |
| tool(ツール) | エージェントが使う、呼び出し可能な能力(シェル、検索、MCP サーバー)。製品名を指すものではありません。 |
| vendor adapter(ベンダーアダプタ) | ブートプロファイルへ誘導するだけで、正典の内容を持たないエージェントホストのエントリポイントファイル。ホストをまたいで使えるもの(AGENTS.md)もあれば、単一のホスト向けのもの(CLAUDE.md、.cursor/rules)もあります。規則はどちらも同じで、違いはツールが既定で何を生成するかだけです。 |
| boot profile(ブートプロファイル) | コンテキストレイヤーの、エージェントに依存しないエントリポイント。人にとってもエージェントにとっても同じです。 |
| agent profile(エージェントプロファイル) | エージェント向けの、ロールごとの読み込みと姿勢を定めた文書。 |
| AI | 形容詞的に(AI ネイティブ)、および流れの名称である人から人へ、人から AI へ、人から AI を経て人への中で使われます。流れの名称における「AI」は、エージェントホストを通じて動作するエージェントを指します。 |
| model(モデル) | エージェントが動く基盤となる予測エンジン。コンテキストレイヤーを読むのはモデルではなくエージェントです。エンジンを行為者と区別しなければならない場合(たとえばホスト固有の仕組みとしてのモデル選択)にのみ現れます。 |
階層を一行で表すと、モデルがエージェントを動かし、エージェントがエージェントホストを通じて動作し、ツールを呼び出します。コンテキストレイヤーが対象とするのはエージェントとホストであり、モデルを直接対象にはしません。この仕様は、このスタックのどの層に対しても中立です。使用するモデル、動作するエージェント、経由するホストにかかわらず、参照するコンテキストレイヤーは同じです。「LLM」を用語に含めていないのは意図的です。これはモデルの一種を指す語であり、この仕様は同じ原則に基づいてモデル中立だからです。
適用範囲の境界。 Leji 1.0 が統制するのは、エージェントと、リポジトリのコンテキストを読み込むエージェントホストです。エージェント的でない AI(補完、インライン提案、リポジトリのコンテキストを持たないチャット)は、コンテキストレイヤーを読み込むエージェントホストの一部として動作する場合を除き、規範の対象外です。
規範的文書#
読む順に並べます。
| 文書 | 定めるもの |
|---|---|
| context-layer.md | コンテキストレイヤー、マニフェスト、ルート、ベンダーアダプタの規則 |
| content-categories.md | 5 つの論理的なコンテンツカテゴリと、インデックスファイルが内容をそれらに割り当てる方法 |
| boot-profile.md | すべてのエージェントホストが読み込む、エージェントに依存しないエントリポイント |
| machine-readable-surface.md | マニフェスト、インデックス、変更履歴、プロファイル、決定記録 |
| decisions.md | 決定記録 |
| governance.md | 提案と承認、所有、収録と削除、鮮度 |
| distribution.md | モノレポ、複数リポジトリのサブモジュール、フェデレーション |
| conformance.md | 4 つの適合レベルとチェックリスト |
| versioning.md | 仕様とスキーマのバージョニング |
../schemas/ にある JSON Schema は、機械可読な成果物について規範的です。../rationale/ と ../adoption/ にある文書は非規範的です。
1.0 の適用範囲#
範囲に含まれるもの: コンテキストの提供、制約の設定、決定の記録、変更のレビュー、再利用可能なパターンの収集。エージェントに依存しない接続とベンダーアダプタ(限定的)。所有と継続性の意味づけ(限定的)。
拡張の境界。 Leji 1.0 が規定するのは、正典となる共有コンテキストレイヤーです。すなわち、チームのコンテキストをどのように記述し、所有し、バージョン管理し、提案し、承認し、インデックスし、読むかです。そのコンテキストレイヤーの周囲で動く実行プロトコル、つまりタスクエンベロープ、一般化された証拠のプロトコル、エージェント間の引き継ぎ、ツール権限のプロトコル、オーケストレーションは、意図的に規定しません。これらは前提条件ではなく拡張プロトコルです。1.0 に適合するコンテキストレイヤーは、それらがなくても有用であり続けなければなりません(MUST)。また実装は、コンテキストレイヤーを読み、提案し、レビューし、承認し、検証するために、それらを必要としてはなりません(MUST NOT)。これらは、実際の運用によって有効性が示された時点で、この言語を完成させるものです。机上で考案するものではありません。
Leji はプログラミング言語でも、DSL でも、ランタイムでも、SaaS でもありません。markdown の規約、小さな JSON スキーマ、ガバナンスの意味づけから成ります。
コンテキストレイヤー
Leji コンテキストレイヤーとは、チームが自分たちの仕事をどう捉えているかを表す、人が読める文書の集合です。バージョン管理され、統制されています。ドメインの言葉、システムの不変条件、規約、ガードレール、決定記録が含まれます。人もエージェントも実際の仕事の中でこれを読み、同じレビューのゲートを通じて変更を提案します。履歴、現在性、承認を継続的に検証できるよう、バージョン管理下に置かれます。その仕組みは以下の要件、関係者の関わり方は参加で説明します。
参加#
コンテキストレイヤーへの参加は、ツールではなくロールに基づきます。リポジトリにおけるレビューと承認の意味が保たれるインターフェースであれば、読む、提案する、レビューする、承認するといった行為をどのインターフェースから行っても構いません。参加に git やコマンドラインを直接扱う知識は必要ありません。
- アクセスできる者は全員が読みます。ここでのアクセスとは、チームの通常のツールを通じた実質的なアクセスであって、リポジトリへのシェルアクセスのことではありません。
- 誰でも提案し、承認するのは人です。提案とは、コンテキストレイヤーを変更するための意図的な要求です。人が直接書いても、人の依頼を受けてエージェントが生成しても、観察した作業を基にエージェントが生成しても構いません(MAY)。実際には、コンテキスト変更の多くをエージェントが記述します。人にしかできない寄与はガバナンス、すなわち意図の提案と、何を正典とするかの承認です。変更を承認する人が説明責任を負うのは、その意味と帰結であり、自らバージョン管理システムを操作することではありません。
- 意味は人のもの、サーフェスは機械のもの。チームの運用上のコンテキストについて規範となる出典は、人が読める文書です。機械可読なファイル(マニフェスト、インデックス、変更履歴)は、ツールがその意味を見つけ、インデックスし、検証し、同期できるようにするために存在します。それらが意味に取って代わることはありません。
これらの流れの規範的な形、すなわち輪(全員が読み、誰でも提案し、人が承認する)は governance.md で定義されます。
記録を読む#
統制された内容には、content-categories.md で定義される 2 つの種類があります。現在の真実として維持される意図と、後の状態によって訂正されるのではなく置き換えられる、日付付きの証拠である記録です。どちらも同じように統制されます。異なるのは、読み手が読み込んだ内容をどう扱えるかです。読み手は、記録を現在の意図として扱ってはなりません(MUST NOT)。記録は、記載された境界内で真である証拠として情報を提供します。記録の主張であることを明示せずに現在の状態として提示すると、読み手はその文書にない現在性を作り出すことになります。これは、以下の縮退モードの規則と同じ構図です。いずれの場合も読み手には、自分がどの種類の現在性を得ているかを把握し、それを明示する義務があります。
要件#
-
コンテキストレイヤーは git リポジトリの中に置かれなければならず(MUST)、それが説明する仕事と一緒にバージョン管理されなければなりません(MUST。同じリポジトリでも、distribution.md に従って利用される専用のコンテキストリポジトリでも構いません)。コンテキストレイヤーの履歴、チェックアウトの現在性(手元の写しが正典のリポジトリと一致しているか)、追記のみの変更履歴の完全性を検証可能にしているのが git リポジトリであり、適合するツールはその 3 つすべてをそこから導きます。そのリポジトリなしにコンテキストレイヤーを読むことは、読み取りモードで定義される、対応はされているが縮退したモードです。
-
Leji を採用するリポジトリは、リポジトリルートにマニフェストファイル
leji.jsonを持たなければならず(MUST)、それはcontext-manifest.schema.jsonに照らして妥当でなければなりません。マニフェストは機械向けのエントリポイントです。仕様バージョン(自らを名乗るlejiキー)、コンテキストレイヤーの名前、コンテキストルート、ブートプロファイルのパス、カテゴリの割り当て、任意の適合性の宣言、そして所有を宣言します。ロール識別子(たとえばthought-partner、reviewer)をエージェントプロファイル文書に結び付けるagentsマップを持っても構いません(MAY)。プロトコルがロールを関与させ、そのロールを誰が担うかはこのマップが決めます。このマップはロールの一覧であって読み込み順ではありません。defaultキーのものも含め、バインディングがプロファイルを読ませることは決してなく、それを行うのはブートプロファイルの Loading セクションだけです。 -
マニフェストはアクター、すなわちロールを担える名前付きの参加者を宣言しても構いません(MAY)。各アクターは、自分が担えるロールと、ロールごとのコマンドテンプレートを宣言します。コマンドをロールで引くことが要点です。ひとつのアクターでも、どのロールを担うかによって必要な起動方法が変わりうるので、アクターごとに 1 つのコマンドでは表現できません。アクターが宣言するロールと、そのコマンドのキーは、同じ集合でなければなりません(MUST)。あるロールにアクターがいる場合、そのロールに結び付いたエージェントプロファイルは
invocationを併せて宣言してはなりません(MUST NOT)。優先順位の定めのない権威あるコマンドが 2 つあることは矛盾であり、レイヤーはコマンドを 1 か所で宣言することでそれを解消します。アクターは任意であり、ほとんどのレイヤーには不要です。それが役に立つのは、ひとつのロールに担える者が複数いる場合か、ひとつのアクターが担うロールによって別の起動方法を必要とする場合です。どちらか一方でも十分な理由になります。担い手がひとりで、必要なコマンドもひとつであるロールには、プロファイル自身のhostとinvocationで足ります。アクターを宣言しても権限は与えられません。それが言うのは、誰にロールを頼めるかであって、誰が承認してよいかではありません。コマンドテンプレートは、どこに現れるものであっても(アクターの
commandsの値でも、エージェントプロファイルのinvocation.commandでも)ひとつの規則に従います。テンプレートは、呼び出す側が選んだシェル向けのコマンドラインです。シェルの形をとらない関与の仕方(構造化された argv 呼び出し、プロセス内での起動)は、1.0 系列ではこれらのフィールドでは表現できません。すべてのテンプレートは<prompt>プレースホルダーを含まなければならず(MUST)、その各出現は、引用符の中や他のテキストと結合した形ではなく、引数の位置にある引用符のないひとつのシェル語として立たなければなりません(MUST)。置換は 1 回限りです。書かれたテンプレートに存在する出現が、同時に、ちょうど一度だけ置き換えられるので、プロンプト本文の中にある文字列<prompt>はデータのまま残り、再展開されることはありません。受け渡しは呼び出す側の責任であり、契約となるのは引用の手順ではなく、求められる結果です。すなわち各出現は、値がプロンプト本文と等しい引数をちょうど 1 つ生み、その一部がシェルの構文として評価されることはありません。スキーマが確かめるのは置換箇所の存在です。配置と受け渡しは、この規則が要求し、呼び出す側が守るべきものです。 -
マニフェストはコンテキストルート(
rootPath)を宣言しなければなりません(MUST)。推奨される既定値はdocs/です。コンテキストレイヤーのパスはすべて POSIX 形式で、リポジトリルートからの相対です。rootPathはコンテキストレイヤーがどこにあるかを宣言するものであって、自らが管轄するパスの基点を変えるものではありません。インデックスのエントリ、ピン留めしたページ、プロファイルのパスをはじめ、Leji のあらゆる成果物のその他すべてのパスは、rootPathの接頭辞を繰り返しているものも含め、リポジトリルートから解決されます。ビューアーのhomepage、logo、faviconは例外です。これらはコンテキストルートからの相対で書かれ、その下に収まるリポジトリルートからの相対パスも受け付けられます。カテゴリとmachineのパスはrootPathの下にあるべきです(SHOULD)。そうでない場合、バリデーターは警告します。 -
コンテキストレイヤーは、boot-profile.md に従うブートプロファイルを持たなければなりません(MUST)。推奨される既定の場所は
docs/boot-profile.mdです。実際の場所はマニフェストのbootProfilePathが宣言します。 -
コンテキストレイヤーの内容は、まず人が読めるものでなければなりません(MUST)。散文には markdown が推奨される形式です。構造化されたメタデータには YAML フロントマターか、machine-readable-surface.md で定義される JSON 成果物を使います。機械にしか読めない文書は、コンテキストレイヤーに属しません。
-
コンテキストレイヤーには、名前の分かる所有者(マニフェストの
owners.primary)がいなければなりません(MUST)。その内容がいまのものであることに責任を負う人です。所有者のいないコンテキストレイヤーは腐ります。
読み取りモード#
コンテキストレイヤーは 2 つのモードで読まれます。保証が異なるので、読み手は自分がどちらのモードにいるかを知らなければなりません(MUST)。読み手は、何を解決できるかから自分のモードを判断します。リポジトリルートに到達可能な leji.json があり、かつ git のワーキングツリーかホストプラットフォームのリポジトリのリビジョン識別のどちらかがある場合は正典です。そのどちらもなく、ただのファイルとして到達した内容は縮退です。
- 正典(canonical)。読み手は、git リポジトリを通じてコンテキストレイヤーを解決します。チェックアウトか、ホストプラットフォームのリポジトリビューです。履歴、チェックアウトの現在性、変更履歴の完全性が検証可能であり、承認された内容は読んだリビジョンの時点でいまのものだと分かります。
- 縮退(degraded)。読み手は、アクセス可能な git のワーキングツリーもバージョンのメタデータもない、ただのファイル内容としてコンテキストレイヤーに到達します。リポジトリを伴わずに、別のインターフェースへアップロード、同期、コピーされたファイルです。ただのファイルとして読むことは、読むことについては一級です(文書は要件により人が読めるものであり、機械可読な変更履歴は宣言された新しさをなお伝えます)。しかし縮退した読み手は、チェックアウトの現在性と承認の状態を未知として扱わなければならず(MUST)、いまのものとして扱ってはなりません(governance.md の鮮度を参照)。このモードでは、変更履歴が持ち運び可能な、宣言された新しさのサーフェスです。それ自体が、その写しが正典のリポジトリと一致していることを立証するわけではありません。
縮退した読み取りは、誰が何をコンテキストレイヤーの読み手にできるかを広げますが、正典の権威への道になることは決してありません。変更が正典になるのは、git に支えられたレビューのゲートを通ったときだけです。また縮退した写しは、フェデレーションが依存する正典モードのチェック(ピンの現在性、古いピンの状態、所有の健全性、distribution.md に従う制限付きマウントのアクセス)を満たせません。
ベンダーアダプタの規則#
エージェントホストの設定ファイル(たとえば CLAUDE.md、AGENTS.md、GEMINI.md、.cursorrules、.cursor/rules、.windsurfrules、.github/copilot-instructions.md)について。
- 正典のコンテキストレイヤーの内容を持ってはなりません(MUST NOT)。
- 存在する場合、ブートプロファイルへ誘導しなければなりません(MUST。通常は一行のポインタです)。
- そのエージェントホストの外では意味を持たないホスト固有の仕組み(モデル選択、ランナーの設定)を持っても構いません(MAY)。ただし、チームの知識がそこに置かれないことが条件です。
- ツールは、どのエントリポイントを確認すべきかを 2 つの出典から知ります。マニフェストの任意の
vendorAdaptersの一覧と、公開された周知の集合(上に挙げたファイル群)です。上の例の一覧が、この系列における周知の集合です。エントリポイントがそこに含まれないホストは、マニフェストがvendorAdaptersでそれを名指ししたときにだけ確認されます。
既存のエントリポイントの慣習は、エージェントホストにどこを見るかを伝えます。Leji が定めるのは、エージェントがそこで何を見つけるかです。出典はひとつで、すべての参加者がそれを読みます。
コンテキストレイヤーではないもの(非規範的)#
- ウィキではありません。ウィキには、最新の状態を保つ仕組みがありません。コンテキストレイヤーが機能し続けるのは、エージェントが作業のたびに読み(誤ったコンテキストは、すぐに問題だと分かる誤出力を生みます)、変更がコードと同じようにレビューされ、ツールによって古さが可視化されるからです。
- 従来の意味でのドキュメントではありません。ドキュメントは、システムが何をするかを事後的に説明します。コンテキストレイヤーは、チームがどう考えているかを現在形で表し、人にもエージェントにも継続的に読まれます。
- 取り込むテンプレートではありません。借りてきたコンテキストは、すぐに古くなります。コンテキストレイヤーの価値は、そのチーム自身の考えが表現されていることにあります。Leji が標準化するのは形とガバナンスであり、内容ではありません。
コンテンツカテゴリ
Leji は、5 つの論理的なコンテンツカテゴリを定義します。分類の基準は、文書が何のためにあるかであり、どこに置かれているかではありません。カテゴリ名は、マニフェスト、インデックス、ツールで使われる安定した識別子です。ディレクトリ名はチームが自由に決められます。
5 つのカテゴリ#
| カテゴリ | そこに属するもの |
|---|---|
domain |
ビジネスの言葉と製品の意味づけを、チーム自身の言葉で。中心となる名詞が何を意味し、互いにどう関係し、どの語がその現場だけの意味を持つか。ビジネスの状態の記録(案件のステータス、市場のスナップショット)も、記録としてここに分類されます。 |
system |
アーキテクチャとその不変条件。サービスの境界、データの所有、統合の契約、一貫性のモデル、失敗時の契約、すべての変更が付き合う制約。技術的な評価やシステムの報告も、記録としてここに分類されます。 |
practice |
自動的に適用される規約とパターン。コードの規約、テストのパターン、そして効果が確かめられたプロンプトやワークフローのパターン(下の収集のゲートを参照)。ある手法を適用した記録(振り返り、ランブックの実行ログ)も、記録としてここに分類されます。 |
governance |
エージェントのガードレールと運用の規則。エージェントが指示なしに何をしてよいか、何に人のゲートが要るか、データの取り扱い規則、エスカレーションの契機、コンプライアンスの統制。ガバナンスの証拠(監査ログ、レビュー報告)も、記録としてここに分類されます。 |
decisions |
物事がいまの形である理由の、日付のある記録。decisions.md に従います。 |
意図と記録#
統制される文書はすべて、カテゴリとは別に、意図か記録のいずれかに分類されます。
- 意図は、現在の真実として維持されるものです。用語集、不変条件、規約、ガードレールなどが該当します。読み手はこれを現行の情報として頼るため、現実が変われば文書を更新します。レビューの期限と鮮度の仕組みは、意図のためにあります(governance.md を参照)。
- 記録は、明示された時点または出来事の範囲内で主張を保存します。ステータス、評価、台帳、報告、会議の結論、アーカイブなどが該当します。後の状態は記録を訂正するのではなく取って代わり、元の記録はその時点の妥当な説明として残ります。記録がいつ時点のものかを示すのは、その日付であり、レビューの期限ではありません。
分類は、ひとつの問いで判断できます。後から得た情報がこの文書と食い違った場合、読み手が現在の情報として頼っているため文書を直す必要があるのか。それとも、新しい情報が文書に取って代わり、元の文書はその時点の妥当な説明として残るのか。 直すなら意図、取って代わるなら記録です。
記録は意図とまったく同じように統制されます。インデックスされ、レビューされ、所有者を持ち、経路が定められます。違うのは、読み手がそれをどう扱ってよいかです。読み手は、記録をいまの意図として扱ってはなりません(MUST NOT)。それは日付のある証拠です(context-layer.md の「記録を読む」を参照)。決定記録は、正式な記録の下位種です。本質的に記録であり、decisions.md に従う独自のスキーマとライフサイクルを持ちます。
記録に関するいくつかの論点は、意図的に 1.0 の対象外とされ、そのこと自体も明示されています。記録の系列という機械的な概念はありません(したがって、ツールがどの記録が「最新」かを認定することもありません)。ストリームの新しさを扱う仕組み(次に来るはずの記録が遅れているかどうか)もありません。意図と記録の内容が実質的に混在する文書について、セクション単位で種別を指定する仕組みもありません。混在する文書は分割すべきです(SHOULD)。分割の負担に見合わない場合は、下流の読み手が主に頼る契約に基づいて分類してください。どのカテゴリにも適切に収まらない内容は、参考資料のままにします。分類に判断が不要だと保証するものではありません。
要件#
- マニフェストは、宣言する各カテゴリを、リポジトリルートからの相対パスで表される 1 つ以上のインデックスファイル(
categories.<id>.indexes)に割り当てなければなりません(MUST)。各インデックスファイルは、context-layer.md に従って、宣言されたコンテキストルートの下にあるべきです(SHOULD)。インデックスファイルが宣言するのは収録であって、移動ではありません。内容はチームがすでに置いている場所(たとえばbusiness/、technology/、architecture/)に残り、ひとつのディレクトリが、何の名前も変えずに複数のカテゴリへ文書を提供できます。 - インデックスファイルは、
leji-indexのフェンス付きコードブロックを 1 つ以上持つ、人が選んで書いた markdown です。ブロックは、3 つ以上のバッククォートに続けてそのブロックの info 文字列を書いた行で開き、次の 3 つ以上のバッククォートの行で閉じます。閉じフェンスのバッククォートの数が開きフェンスと一致している必要はありません。妥当な info 文字列はちょうど 3 つです。leji-index(意図のブロック)、leji-index intent(同じものを明示したもの)、leji-index record(記録のブロックで、その各エントリは記録として解決されます)。leji-indexの後にそれ以外のトークンが続くものはパースエラーであり、黙って無視されることはありません。この文法は意図して有限です。各ブロックは内容を 1 行に 1 エントリ、- path: <リポジトリルートからの相対パス>の形で列挙します。パスはディレクトリ(その下の markdown が再帰的に含まれます)か、markdown ファイル 1 つです。パスはリポジトリルートからの相対の POSIX 形式でなければなりません(MUST)。先頭の/、..のセグメント、バックスラッシュは不正であり、拒否されます。空行と行全体の#コメントは無視され、エントリは末尾に空白を前置した# コメントを持っても構いません(MAY)。この文法における空白は ASCII のスペース(U+0020)とタブ(U+0009)だけです。これは、文法が空白を参照するすべての場所、すなわちフェンスのバッククォートと info 文字列の周り、エントリ行の前後の空白、末尾コメントを開く#の前にも当てはまります。先頭の UTF-8 バイトオーダーマークは、パースの前に取り除かれます。行は LF で分割され、末尾の CR は許容されます。ファイルは UTF-8 です。実装はここでランタイムの空白クラスを使ってはなりません(MUST NOT)。ランタイムがたまたま空白に分類するそれ以外の文字は、U+0085 と U+00A0 を含めて通常のパスの内容であり、そうした文字を含むパスを持つエントリは、黙って切り詰められるのではなく、見つからないものとして報告されます。boot-profile.md のleji-mountsブロックも同じ文字集合に固定されているので、ひとつのスキャナが両方の文法を読め、3 つの実装がフェンスの有無について食い違うことはありません。ひとつのファイルの中の複数のブロックは、文書の順に連結されます。ブロックの周りに散文や見出しを置いてよいので、インデックスファイルは人が読めるそのカテゴリの地図も兼ねます。走査は行単位で、markdown の構造は参照しません。任意のスペースまたはタブの字下げのあとに 3 つ以上のバッククォートとタグを持つ行は、より長いフェンス付きの例の中でも、リスト項目の中でも、文書のどこにあっても本物のブロックを開きます。したがって、宣言ではなく説明のための例は別のタグでフェンスします。leji-indexの後にトークンを足すのではありません。スキャナが照合するのはタグなので、leji-index exampleは本物のブロックを開いてパースエラーを報告し、textとタグ付けされたフェンスは何も開きません。推奨される場所は、コンテキストルート下のcontext/<id>.mdです。場所は設定可能であり、ツールがそれを固定的に埋め込むことはありません。 - コンテキストレイヤーは、いずれかの適合レベルを宣言するために、少なくとも
domainかsystemのどちらかと、decisionsを割り当てなければなりません(MUST。conformance.md を参照)。また、内容のあるdomain/systemの最低要件には、少なくとも 1 つの意図の文書が含まれなければなりません(MUST)。記録だけのコンテキストレイヤーは、履歴は保存しますが、運用上のコンテキストを持ちません。他のカテゴリは、チームが実際の問いにぶつかるにつれて積み上がります。空のカテゴリ(インデックスファイルがどの文書にも解決しないもの)を、チェックリストを満たすために割り当ててはなりません(MUST NOT)。 - 文書は、ちょうど 1 つのカテゴリと 1 つの種別に解決されます。インデックスのエントリはセレクタであり、解決はセレクタの限定度に従います。ファイルを直接指すセレクタは、あらゆるディレクトリのセレクタに勝ち、より深いディレクトリのセレクタは、その祖先のディレクトリのセレクタに勝ちます。ある文書を覆うもっとも限定的なセレクタが、その文書のカテゴリとブロックの種別を決めます。より広いセレクタが覆っていても、より限定的なセレクタが勝った文書は、単にその広いセレクタの内容ではなくなります(記録のディレクトリの中にある、いまのものとして維持される 1 ファイルや、より広く割り当てられたツリーの中にあるひとつのチームの決定ログは、こうして何も動かさずに表現されます)。カテゴリまたは種別について食い違う同じ限定度のセレクタはエラーであり、インデックスの順序で解決されることは決してありません。同じ限定度で同一の割り当てをするものは一度だけ解決され、ひとつのインデックスファイルの中で文字どおり重複したエントリは拒否されます。ツールは、覆っているすべての文書がより限定的なセレクタに取られてしまったセレクタ(影に隠れたセレクタ)を示すべきです(SHOULD)。それは人が書いた地図に残る不要な記述であって、エラーではありません。それ以外の点で解決は決定的です。ディレクトリのエントリは、その下の markdown へ POSIX の辞書順(Unicode コードポイント順。実装をまたいで順序が一意になるよう、パスは ASCII にとどめることが推奨されます)に展開され、実際の場所(シンボリックリンクを解決したあと)がリポジトリルートの外に出るパスは、たどられるのではなく除外されます。インデックスのエントリ(machine-readable-surface.md を参照)は、カテゴリ識別子と種別を持ちます。
- 文書は、自らの種別をフロントマターで宣言しても構いません(MAY。
kind: intentまたはkind: record)。フロントマターは、勝ったセレクタのブロック種別を上書きしますが、カテゴリを上書きすることは決してありません。それ以外のkindの値はエラーです。決定記録はkindキーを取りません(そのスキーマは閉じており、本質的に記録だからです)。記録はフロントマターのdate(YYYY-MM-DD)を持っても構いません(MAY)。ツールが記録の日付を読むのはその項目からだけであり、散文、見出しの慣習、ファイル名から読むことはありません。記録はfreshness.reviewAfterを持ってはなりません(MUST NOT)。レビューの期限は意図のための仕組みであり、記録に付けると、その文書が持ち得ない現在性を約束することになるので、エラーです。 - プロンプトやワークフローのパターンを記した practice の内容は、そのパターンが少なくとも二度うまくいったあとにだけ収集すべきです(SHOULD。二度実証のゲート)。早すぎる収集こそが、practice のディレクトリを願望で埋める原因です。
補足(非規範的)#
初日からすべてのカテゴリを揃える必要はありません。最小限のコンテキストレイヤーに必要なのは、最初の 1 か月の仕事で実際に頼る内容です。カテゴリを設ける目的は、人やエージェントが「これはどの種類の真実か」を判断し、ツリー全体ではなく、目の前の作業に有効な部分だけを読み込めるようにすることです。
2 つの種別を設けているのは、実際のリポジトリにあるドキュメントには、真実の捉え方が異なる 2 種類の情報が混在するからです。運用に関する情報まで意図として扱うと、どちらもうまく機能しません。守れない鮮度の保証が生まれるか、リポジトリの大半がガバナンスの対象外になるかのどちらかです。記録用ディレクトリ内に意図の例外を 1 つ置く場合、実際の形は次のようになります。
# Domain context
```leji-index
- path: docs/glossary.md
```
Operational state is governed as records; the escalation policy stays intent.
```leji-index record
- path: docs/operations/
```
```leji-index intent
- path: docs/operations/escalation-policy.md
```
ブートプロファイル
ブートプロファイルは、コンテキストレイヤーに設ける、エージェントに依存しないエントリポイントです。すべてのエージェントホストと人が共通の起点として使える、人が読める単一の文書です。「このコンテキストレイヤーは何か」「何を読み込むのか」「ここでどう振る舞うのか」に答えます。
要件#
-
コンテキストレイヤーは、マニフェストの
bootProfilePathが宣言するパスに、ちょうど 1 つのブートプロファイルを持たなければなりません(MUST)。推奨される既定値はdocs/boot-profile.mdです。 -
ブートプロファイルは、ツールなしで人が読める素の markdown でなければなりません(MUST)。いかなるベンダーの設定構文にも依存してはなりません(MUST NOT)。
-
ブートプロファイルは、次を扱わなければなりません(MUST)。
- Identity(概要):このリポジトリまたは製品が何であるかを、1 段落で記述します。
- Loading(読み込み):どの種類の作業で、どのコンテキストを読むか。まず無条件の集合(どの作業の前にも読むもの)を示し、続いて、パス、カテゴリ、またはコンテキストインデックスによって経路を決める作業種別ごとのセレクタと、どのセレクタにも当てはまらない作業向けに定義されたフォールバックを示さなければなりません(MUST)。これは、Task routing のアルゴリズム(machine-readable-surface.md)を、実際の作業で使う言葉によってブートプロファイルの水準で表したものです。従うために、そのアルゴリズムの知識は必要ありません。
- Posture(姿勢):エージェントに期待する運用上の振る舞い(いつ進めてよいか、いつ質問するか、決してしてはならないこと)。これは、ガバナンスの内容や core のエージェントプロファイルへの参照として持たせても構いません(MAY)。
-
ブートプロファイルは、マニフェスト、インデックス(あれば)、エージェントプロファイル(あれば)へリンクすべきです(SHOULD)。どのホストから入ったエージェントでも、機械可読なサーフェス全体を見つけられるようにするためです。
-
ブートプロファイルは、実際の作業で使う言葉で記述しなければなりません(MUST)。実際のパスと具体的な読み込み順を明示し、それに従うためにこの仕様の知識を必要としないようにします。マニフェストとスキーマはツールのためのものであり、エージェントのためのものではありません。従うために仕様の理解が必要なブートプロファイルは、適合性に問題がある可能性を示します。
-
ブートプロファイルは、コンテキストレイヤーを保守する責務として、変更の記録先(宣言された変更履歴)と、決定の残し方(宣言された決定記録の場所)を記述すべきです(SHOULD)。ブートプロファイルがいずれにも触れていない場合、バリデーターは警告します。
-
ベンダーのエントリポイントファイルは、context-layer.md のベンダーアダプタの規則に従ってブートプロファイルへ誘導します。
-
ブートプロファイルの無条件の読み込み集合(どの作業の前にも読むと述べているもの)は、すべての作業が必要とするものに限るべきです(SHOULD)。一部の作業しか必要としないコンテキストは、あらかじめ読み込むのではなく、作業、カテゴリ、インデックスによって経路を決めるべきです(SHOULD)。また決定記録は、際限なく積み上がるので、ディレクトリごと読み込むのではなく、宣言された
affectedPaths/affectedCategoriesによって経路を決めるべきです(SHOULD)。無条件の集合にあるものは、毎回の作業でその読み込みコストを負担することになります。 -
フェデレーションされた兄弟。
federation.mountsを宣言するコンテキストレイヤー(distribution.md に従うもの)は、それらの兄弟を、機械が確認できる形でブートプロファイルに示さなければなりません(MUST)。info 文字列がleji-mountsであるフェンス付きブロックを 1 つ以上、文書のどこに置いてもよく、その各エントリは文書の順に連結され、宣言された各マウントにつきちょうど 1 エントリを持ちます。エントリは、その兄弟、所有者、それが何を運ぶか、いつ読むかを名指しします。後ろの 2 つは書き手の作業の言葉で書きます。実際の例は要件のあとにあります。すべての実装が同一に読めるよう、文法は固定されています。ブロックは、3 つ以上のバッククォートに続けて info 文字列を書いた行で開き、次の 3 つ以上のバッククォートの行で閉じます。閉じフェンスのバッククォートの数が開きフェンスと一致している必要はありません。info 文字列は
leji-mounts単独です。その後にトークンを持つフェンスはエラーであって、無視されるフェンスではありません。フェンスの行はスペースまたはタブの字下げや前後の空白を持っても構いませんが(MAY)、その間のレコードは持ってはなりません(MUST NOT)。レコードは 1 桁目の- mount:で始まり、そのフィールドはちょうど 2 つの ASCII スペースで字下げされます。ひとつのレコードの中でowner、carries、read-whenはそれぞれちょうど一度、順不同で現れます。未知のフィールド、重複したフィールド、欠けたフィールドはエラーです。値は、key:接頭辞のあとの行の残り(空でないもの)であり、前後にスペースやタブはなく、制御文字や行区切り文字も含みません。この文法における空白は ASCII のスペース(U+0020)とタブ(U+0009)だけであり、フェンス行の字下げと前後の空白でも、内容行でも同じです。実装はここでランタイムの空白クラスを使ってはなりません(MUST NOT)。それらは U+0085 や U+00A0 のような文字について食い違い、ブロックが存在するかどうかについて食い違うことになるからです。先頭の UTF-8 バイトオーダーマークは、パースの前に取り除かれます。行は LF で分割され、末尾の CR は許容されます。空行と#で始まる行全体は無視されます(content-categories.md のカテゴリインデックスのブロックと同じです)。ファイルは UTF-8 です。走査は行単位で、markdown の構造は参照しません。任意のスペースまたはタブの字下げのあとに 3 つ以上のバッククォートとタグを持つ行は、より長いフェンス付きの例の中でも、リスト項目の中でも、文書のどこにあっても本物のブロックを開きます。したがって、宣言ではなく説明のための例は別のタグでフェンスします。leji-mountsの後にトークンを足すのではありません。スキャナが照合するのはタグなので、leji-mounts exampleは本物のブロックを開いてパースエラーを報告し、textとタグ付けされたフェンスは何も開きません。mountは宣言されたマウントのnameと一致しなければならず(MUST)、ownerはそのマウントの宣言されたowner.nameと一致しなければなりません(MUST)。比較はデコードされた文字列として行われます。宣言されていないマウントのエントリ、ひとつのマウントに対する 2 つ目のエントリ、エントリのない宣言済みマウントは、いずれもエラーです。マウントを宣言していないレイヤーは、leji-mountsブロックを持ってはなりません(MUST NOT)。兄弟の場所は、意図して要素になっていません。マウントは、機械ごとの、内容アドレスによる投影として実体化されるので、読み手はパスを推測するのではなく
leji mounts locate <name>で解決します(distribution.md に従います)。ブロックの周りの散文は、経路の決め方を自然な言葉で説明すべきです(SHOULD)。ブロックは確認できる核であって、その散文やleji.jsonの宣言に取って代わるものでは決してありません。マウントされた兄弟は、名前を持つ別個の出典であり、ホストのカテゴリに統合されることはありません。ブートプロファイルがエージェントを兄弟へ導くのは、作業がその経路の条件に当てはまるか、プロファイルがそれを求めるときだけです。確認されないままの部分は意図的です。carriesとread-whenは自由記述であり、それがマウントの経路のメタデータに忠実かどうかは、ツールが検証するのではなくチームが申告します。ツールが確認するのは、列挙、同定、存在です。ここで兄弟を示すことで、マウントの発見がエージェントにとって作業の言葉のエントリポイントにとどまり、要件 5 に従うのにマニフェストを読む必要がないままになります。
leji-mounts ブロックの実例#
acme-product-context という名前のマウントを 1 つ宣言しているホストのための、エントリ 1 件です。ブロックはブートプロファイルの 1 桁目に、ここに見えるとおりに置かれます。外側のバッククォート 4 つのフェンスはこの文書の包みであって、ブロックの一部ではありません。
```leji-mounts
- mount: acme-product-context
owner: Product team
carries: product-side domain language and the decisions behind the customer-facing surface
read-when: a task touches product behavior, product terminology, or billing
```
エージェントプロファイル#
コンテキストレイヤーは、machine.agentProfilesPath が宣言するディレクトリの下に、ロールごとのプロファイル(たとえばレビュアーのプロファイル、リリースのプロファイル、QA のプロファイル)を定義しても構いません(MAY)。各プロファイルは次のとおりです。
-
agent-profile.schema.jsonに照らして妥当な YAML フロントマターを持つ markdown でなければなりません(MUST)。 -
継承を解決したあとに、そのロールが最初に読むもの(
requiredRead)と、停止して尋ねなければならない場面(mustAskWhen)を持たなければなりません(MUST)。inheritsを宣言するプロファイルは、基底がそれを供給する場合、どちらか一方を省いても構いません(MAY)。宣言しないプロファイルは、自ら両方を宣言しなければなりません(MUST)。 -
inheritsを宣言しても構いません(MAY)。これは 1.0 系列で有効です。それは、そのレイヤーのプロファイル集合の中の他のプロファイルをちょうど 1 つ名指しします。そのプロファイルのroleはcoreでなければならず(MUST)、このプロファイルはその姿勢と本文を拡張します。レイヤーのプロファイル集合とは、宣言されたmachine.agentProfilesPathの下のすべての文書と、マニフェストのagentsマップが名指しするすべての文書(それがどこにあっても)です。解決は 1 段階だけなので、roleがcoreであるプロファイルはinheritsを宣言してはなりません(MUST NOT)。また名指しされた対象は存在しなければならず(MUST)、idで一意でなければならず(MUST)、自身がinheritsを宣言してはなりません(MUST NOT)。解決は次のように合成します。- 姿勢の配列(
requiredRead、defaultContext、mustAskWhen、mustRefuseWhen): 基底のエントリを書かれた順に、続いて派生プロファイルのエントリをその順に並べ、基底がすでに持つものは落とします。書かれた順序は読み込みの意図なので、並べ替えは行いません。 - それ以外のすべてのフィールド(
id、name、role、purpose、version、host、invocation、escalation、owners、freshness): 派生プロファイル自身のものであり、継承されることは決してありません。inheritsは解決のための指示であり、それ自体は解決後のプロファイルの一部ではありません。 - 本文: どちらの本文も規範的です。基底が先、続いて派生プロファイルです。
継承されたプロファイルを解決できない利用側は、派生ファイルだけを適用してはなりません(MUST NOT)。派生ファイルはプロファイルの半分なので、利用側は代わりにそれを未対応として報告します。尋ねる条件と拒否する条件が同じ状況に当てはまる場合は、拒否が優先します。
解決が保証するのは合成であって、意味の絞り込みではありません。基底と矛盾したり基底を弱めたりする派生の散文は不適合であり、自然言語の矛盾を検出するツールは存在しません。
- 姿勢の配列(
プロファイルが調整するのは、あるロールが何を読み、どう振る舞うかです。コンテキストレイヤーの内容を複製するものではありません。
プロファイルの任意の host と invocation は、担い手がひとりの場合の簡略記法です。このロールを担う唯一の参加者にどう関与するかを述べます。その command は、<prompt> プレースホルダーとその配置を含め、アクターのコマンドテンプレートと同じ規則に従うテンプレートです(context-layer.md の要件を参照)。ひとつのロールに担える参加者が複数いる場合や、同じ参加者が担うロールによって別の起動方法を必要とする場合は、マニフェストの任意の actors レジストリが代わりにそれを持ちます(同じ節を参照)。ロールが使うのはどちらか一方の仕組みであって、両方ではありません。
補足(非規範的)#
ブートプロファイルは、意図的に単調なものです。地図と姿勢を示すものであり、知識を集積する場所ではありません。数画面を超える長さに膨らんでいるなら、本来カテゴリに属する内容がエントリポイントに入り込んでいます。
この設計が防ぐ失敗の形は、間接参照です。エージェントが最初に読むコンテキストから実際の制約までの参照が 1 段増えるたびに、注意が削がれます。適切に実装されたコンテキストレイヤーには、ベンダーのエントリポイントがまったく必要ありません(invocation からブートプロファイルを直接指定できます)。ブートプロファイルから内容へも直接たどれます。深さを持たせるべきなのはコンテキストレイヤーの文書であり、そこへ至る経路ではありません。
ブートプロファイルで「どの作業の前にも読む」と指定した文書には、作業のたびに読み込みコストがかかります。そのため、無条件の集合はコンテキストレイヤーで最もコストの高い場所です。真に普遍的なものだけに絞り、残りは作業種別ごとの読み込み、カテゴリ、インデックス、各決定記録で宣言された適用範囲を通じて導いてください。インデックスの目的は、エージェントがツリー全体ではなく、作業に必要な部分だけを読み込めるようにすることです。決定は際限なく増えるため、ディレクトリ全体を事前に読み込まず、経路によって導きます。
機械可読サーフェス
5 つの成果物によって、ツールからコンテキストレイヤーを読み取れるようになります。それ以外の部分では、人向けの文章をエージェントも読んでいるにすぎません。ツールが扱う契約は、この 5 つです。
| 成果物 | 既定の場所 | スキーマ |
|---|---|---|
| マニフェスト | leji.json(リポジトリルート、固定) |
context-manifest.schema.json |
| コンテキストインデックス | <root>/context-index.json |
context-index.schema.json |
| コンテキスト変更履歴 | <root>/context-changelog.json |
context-changelog.schema.json |
| エージェントプロファイル | <root>/agents/*.md(フロントマター) |
agent-profile.schema.json |
| 決定記録 | <root>/decisions/*.md(フロントマター) |
decision-record.schema.json |
マニフェスト以外の場所はすべてマニフェストが宣言します。表に示したのは既定値です。
要件#
- マニフェスト。
leji.jsonはリポジトリルートに存在しなければならず(MUST)、そのスキーマに照らして妥当でなければなりません。これは Leji で唯一、名前が固定されたファイルです。ツールが確実に探しにいくファイルです。 - インデックス。
indexed以上の適合性を宣言するコンテキストレイヤーは、生成されるものであって、手で維持されるものではないコンテキストインデックスを持たなければなりません(MUST)。ツールは、カテゴリインデックスファイル(categories.<id>.indexes。content-categories.md に従う)を、そこに列挙された文書へ解決し、統制された文書ごとに 1 エントリを書きます。各エントリは、安定したid、path、title、そしてカテゴリ識別子categoryを持ちます。生成器は、その文書のkind(intentまたはrecord)も出力すべきです(SHOULD)。これはスキーマ上は任意なので、種別が存在する前に書かれたインデックスも妥当なままであり、利用側は値がない場合をintentとして扱います。記録のエントリは、その文書が妥当なフロントマターのdateを宣言している場合、そのdateも併せて持ちます。生成器が日付を取るのはフロントマターからだけであり、散文やファイル名の慣習からではありません。古くなったインデックス(インデックスファイルが解決する内容ともはや一致しないもの)は、検証の失敗として扱われなければなりません(MUST)。federation.mountsを宣言するホストは、同じインデックスの中にトップレベルのmounts配列も持ちます。マウントごとに 1 件の経路の記録(name、source、pin、宣言されていればtrackingRef、owner、宣言されていればrole、そして経路のメタデータcategories/topics/requiredWhen)です。これらは経路の記録に限られます。ツールは、兄弟のエントリや散文をホストのインデックスへコピーしてはなりません(MUST NOT)。またマウントの記録は、ホストの読み手が見てはならないものを何も持ちません(distribution.md の「制限付きマウント」に従います)。 - 変更履歴。
indexed以上の適合性を宣言するコンテキストレイヤーは、コンテキストレイヤーの変更についての機械可読な変更履歴を持たなければなりません(MUST)。各エントリは、安定したid、UTC のdate、type、一行のsummary、影響を受けたpathsを持ちます。正典の順序は位置ではなく導出されるものです。ツールはエントリを(date, id)の昇順で並べなければならず(MUST)、配列上の位置には意味がありません。idは変更履歴の中で一意なので(「識別子」を参照)、2 つの変更がdateを共有していても(date, id)は全順序になります。残っているエントリは不変です。ツールは、以前の状態を確かめられる場所であれば、公開済みエントリの変更を検証の失敗として扱わなければなりません(MUST)。配列の並べ替えは変更にあたりません。その状態を確かめるには、比較対象となる別の基準が必要です。通常の継続的インテグレーションのチェックアウトのように、リファレンスのツールが現在のリビジョンしか持たない場合、その変更はツールには見えず、それを捕まえるのは変更一式のレビューです(conformance.md を参照)。変更履歴は新しさのサーフェスであって、アーカイブではありません。長く使われるコンテキストレイヤーは、際限なく育てるのではなくそれを圧縮すべきです(SHOULD)。またいつでも、その順序の最も古い側からエントリを取り除くことで圧縮して構いません(MAY)。ただし、同じ変更一式がcompaction型のエントリを追加し、そのcompactedフィールドに件数と、取り除いた最初と最後の id を記録することが条件です。最も古いエントリ以外の削除、圧縮エントリを伴わない削除、空のファイルになるまでの圧縮は、いずれも検証の失敗です。追記のみの規律はidを鍵とする集合として扱われ、直前にコミットされた状態と照合されるので、書く時点では git が必要です。ファイル自体は利用側にとって git を必要とせず、完全な記録は git の履歴が保持します。統制された文書(カテゴリインデックスファイルが解決する文書)に触れる変更一式は、変更した統制されたパスを覆うpathsを持つエントリを追加しなければなりません(MUST)。こうして、変更されたすべての統制された文書が、追加されたいずれかのエントリの下に入ります。追記のみの規律が公開済みエントリを不変に保ち、この網羅の規則が記録を完全に保ちます。人が読める変更履歴が併存しても構いません(MAY)。ツールが読むのは JSON の記録です。 - フロントマターの成果物。エージェントプロファイルと決定記録は、YAML フロントマターがそれぞれのスキーマに照らして妥当な markdown 文書です。散文の本文は自由な形式のままで、機械との契約はフロントマターです。純粋な JSON のプロファイルや決定を必須としてはなりません(MUST NOT)。これらの文書は人が読むものです。
- 識別子。すべての
idの値は、いったん公開されたら安定でなければなりません(MUST)。改名や移動が更新するのはpathであって、idではありません。識別子は小文字でハイフン区切り、その成果物の種類の中で一意です。生成されたインデックスエントリのidは、次の優先順で導出されます。その文書のフロントマターがidを宣言していればそれ。なければ、保存済みのインデックスが同じパスに対して、あるいは内容を保った純粋な移動については同じ内容に対して、すでに持っているid。それもなければ、親ディレクトリの中で衝突を避けたファイル名のスラグ。最初に見つかったものが勝つので、公開されたidは改名や移動を生き延び、まったく新しい文書だけが新しいものを得ます。ひとつの変更一式の中で移動かつ編集される可能性のある文書は、フロントマターのidを宣言すべきです(SHOULD)。パスと内容が同時に変わる場合に id を固定できるのはフロントマターだけです(パスによる引き継ぎも内容ハッシュによる引き継ぎも、どちらも取り逃します)。保存済みの id が消えたときはツールが警告するので(id-vanished)、それが残す宙に浮いた参照は捕まえられます。 - タイムスタンプ。変更履歴の
dateの値は UTC の ISO 8601 です。暦日YYYY-MM-DD(その日の始まりT00:00:00Zとして順序づけられます)か、Zで終わる秒単位のタイムスタンプ(たとえば2026-06-13T15:04:05Z)のどちらかです。ゾーンのない時刻、UTC 以外のオフセット、秒未満の小数は許されません。秒未満の小数は、dateの辞書順が時系列順になるという保証を壊します。…05.1Zは…05Zより後の時刻なのに前に並ぶからです。あらゆる成果物のあらゆる日付フィールドは暦の範囲で検査されるので、月が13、日が99のものは不正です。他の成果物の日付は ISO 8601 に従い、日付だけでも構いません(MAY)。パスは POSIX 形式で、リポジトリルートからの相対、先頭に./は付けません。 - マニフェスト以外のすべての JSON 成果物は、それが書かれた対象のスキーマ系列(
schemaVersion)を宣言しなければなりません(MUST。versioning.md に従います)。マニフェストは、自らを名乗るlejiキーで対象の仕様系列を宣言します。 - 派生サーフェスはアクセスの制約を引き継ぎます。インデックス、変更履歴、生成されたビューアー、そしてコンテキストレイヤーの内容から作られたあらゆるコンパイル済みまたはエクスポートされたビューは派生サーフェスであり、エージェントがその内容から生み出した出力も同様です。派生サーフェスは、それが引いてきた内容のうちもっとも制限の強いものの、アクセスの制約を帯びます。派生サーフェスは、明示的でレビューされた墨消しの手順を経て、その読み手のための別のサーフェスを作るのでない限り、その内容よりも広い読み手を持つ場所へ書かれたりコピーされたりしてはなりません(MUST NOT)。またエージェントは、制限された文脈を、より広い読み手を持つサーフェスや、より制限の弱いサーフェス(プルリクエスト、チケット、チャット、コミットメッセージ、公開のコンテキストレイヤー)へ引用したり要約したりしてはなりません(MUST NOT)。制限されたコンテキストレイヤーのインデックスは、その散文と同じくらい機微でありえます。タイトルも、パスも、要約も、すべてそれを説明しているからです。これはツールが行うチェックではなく、ツールを操作する人とエージェントに課された制約です。Leji は、ツールが「より広い読み手」を計算するために読める読み手のモデルを定義していないので(アクセスはバージョン管理システムのものです。governance.md の「アクセスの境界」に従います)、リファレンスの SDK はこれを強制せず、どのツールもせいぜい警告するだけです(ビューアーのエクスポートは、非公開でホストするよう警告します)。
Task routing(作業に応じた経路の決定)#
インデックス、カテゴリの割り当て、決定記録の目的は、エージェントがツリー全体ではなく、作業に必要なコンテキストだけを読み込めるようにすることです。この節では、作業の適用範囲からその一部を選ぶ方法を規範的に定義します。仕様の他の箇所から参照される、唯一の経路決定アルゴリズムです。ブートプロファイルの Loading 節(boot-profile.md)は、実際の作業で使う言葉でエージェントをここへ導きます。決定記録の適用範囲(decisions.md)はこのアルゴリズムで照合され、フェデレーションでの読み取り(distribution.md)でも、作業がどの兄弟に関係するかを決めるために再利用されます。経路の決定はコンテキストを読むためのものであり、タスクエンベロープや実行プロトコルではありません。これらは 1.0 の対象外です(README.md の「拡張の境界」を参照)。
- 入力。作業の適用範囲とは、その作業が読むか変更する、リポジトリルートからの相対の POSIX パスの集合(要件 6 に従って正規化されたもの。POSIX 形式、ルートからの相対、先頭に
./を付けない)に、作業が明示的に名指ししたカテゴリと、作業が明示的に名指ししたトピックを加えたものです。トピックは明示的な入力です。アルゴリズムがそれをパス、カテゴリ、散文、内容から導くことはありません。エージェントやツールがどのように作業から適用範囲を導くかは規範の対象外ですが、以下の照合はそうではありません。 - パスの照合(字面のみ、双方向)。宣言されたパスと作業のパスが一致するのは、正規化のあと(POSIX 形式、ルートからの相対、先頭に
./を付けない、末尾の/を取り除く)、2 つの文字列が等しいか、一方が他方のパス接頭辞としての祖先であるときです。すなわち、短いほうが、長いほうを/の境界で切り詰めたものと等しいときです。照合は純粋に字面上のものです。ファイルシステムを参照することはなく、ファイルを指すパスとディレクトリを指すパスを区別しません。正規化のあとでは、その 2 つは見分けがつかないからです。これはどちらの向きにも成り立つ包含関係であり(リファレンス実装が共有するunderPath関係です)、広い適用範囲を持つ作業と、狭く宣言されたセレクタは、どちらが広くても互いを見つけます。 - カテゴリの照合(狭い)と、2 つのカテゴリ集合。作業のカテゴリは、展開される(expanded)ものと合図となる(signalled)ものに分かれます。作業が明示的に名指ししたカテゴリは、両方の集合に入ります。それ自体が統制された文書である作業のパス(生成されたインデックスのエントリと完全一致するもの。包含では決してありません)は、そのエントリのカテゴリを合図となる集合にだけ寄与します。展開されるカテゴリは、その意図の文書と記録の候補を読み込みます。合図となるカテゴリは、決定とフェデレーションのマウントにとっての照合の合図であり、それ自体は何も読み込みません。カテゴリのセレクタは、任意のリポジトリのファイルからカテゴリを推測してはなりません(MUST NOT)。またそれ自体が統制された文書ではない作業のパスは、統制された文書の祖先ディレクトリも含め、カテゴリをまったく寄与しません。パスの適用範囲はファイルに届きますが、カテゴリの展開はそれに追随しません。
- トピックの照合(完全一致、マウント限定)。トピックは Unicode スカラー値からなる空でない文字列で、その UTF-8 表現によって比較されます。単独のサロゲートは妥当なトピックではありません。この規則は双方に適用されます。Unicode スカラー値からなる空でない文字列でない作業のトピックやマウントの
topicsのエントリは入力の誤りであり、実装は黙って不一致として返すのではなく、それを拒否しなければなりません(MUST)。作業のトピックが宣言されたトピックと一致するのは、デコードされた 2 つの文字列が完全に等しいときです。実装は、どちらの側についても、大文字小文字の変換、Unicode 正規化、ロケールに依存する比較、切り詰め、トークン化、部分文字列の照合、あいまい照合を行ってはなりません(MUST NOT)。したがって、正規等価だがバイト列の異なる綴りは一致しません。この等値の規則は、下にある結果のバイト単位の順序づけとは別のものです。作業のトピックが重複していてもひとつの合図になるので、あるトピックを二度名指ししても、一度名指ししたのとまったく同じように照合されます。フェデレーションのマウントは、作業のいずれかのトピックが、そのマウントが宣言するいずれかのトピックと等しいときに一致します。トピックの一致が選ぶのはマウントだけです。展開される集合にも合図となる集合にも入ってはならず、いかなる文書や記録も読み込んではならず、いかなる決定の経路も定めてはならず、requiredWhenを評価してはならず、マウントを必須にしてはなりません(MUST NOT)。 - ステータスによる絞り込み。いまの指針として経路に載るのは、
statusが拘束力を持つ決定記録だけです。acceptedとdeprecatedは拘束力を持ちます。deprecatedの記録は、古びた姿勢を伴って拘束します。エージェントはそれを、確立したいまの実践ではなく、退場の途上にある指針として扱わなければなりません(MUST)。supersededの記録は、履歴として以外は拘束してはならず(MUST NOT)、supersededByを持たなければなりません(MUST)。proposedとrejectedの記録は拘束してはなりません(MUST NOT)。拘束する記録はlive です。 - 適用範囲のない決定。
affectedPathsもaffectedCategoriesも宣言していない live の決定記録は、組織全体に及びます。作業の適用範囲が何であれ、すべての作業に対して経路に載ります。適用範囲を持つ live の決定は、作業がパス(2)またはカテゴリ(3)で一致したときにだけ経路に載ります。 - パスの適用範囲が空の場合と、適用範囲が空の場合。作業のパスの集合が空のとき、パスの照合は何も寄与せず、エージェントはパスに基づく経路の決定が評価されなかったことを述べなければなりません(MUST)。明示的に名指しされたカテゴリはなお尊重され、なお展開されます。明示的に名指しされたトピックもなお照合されます。名指しされたカテゴリと名指しされたトピックは、どちらも適用範囲を空でなくします。作業の適用範囲全体が空になるのは、パスも、カテゴリも、トピックも名指ししていないときだけです。そのときエージェントは、無条件のブートプロファイルとエージェントプロファイルのコンテキストに加えて、組織全体に及ぶ適用範囲のない live の決定だけを経路に載せます。エージェントは、経路の決まっていない読み込みを、適用範囲の定まったものであるかのように提示してはなりません(MUST NOT)。
- 記録は候補として経路に載ります。展開されるカテゴリは、その意図の文書を必須のコンテキストとして経路に載せます。そのカテゴリの記録は、種別と日付を添えて別に返され、読み手が判断して読み込む候補になります。記録が必須になるのは、作業のパスが項目 2 によってそれを直接選んだとき、エージェントかブートプロファイルがそれを名指ししたとき、人がそれを求めたときだけです。カテゴリで一致したことや、いちばん新しい日付を持つことが、記録を必須にすることは決してありません。記録がカテゴリの候補であると同時にパスで直接選ばれている場合は、直接の選択が勝ち、必須になります。経路の決定が、どの記録かを「最新」や「いまのもの」として認定してはなりません(MUST NOT)。1.0 は記録の系列としての同定も順序の保証も定義していないので、新しさの判断は読み手のものであり、インデックスが示す日付に照らして下されます。決定記録は独自の経路(項目 5 と 6)を保ち、一般の記録として経路に載ることはありません。決定のファイルを作業のパスとして名指ししても、その決定が経路に載るわけではありません。載せるのはその宣言された適用範囲です。
- 引用。経路に載った決定記録を読み込んだエージェントは、一致した記録のうちどれを読み込んだかを引用しなければなりません(MUST)。読み手が、エージェントがどの指針を適用したかを見て、何を適用しなかったかを推し量れるようにするためです。
経路の定まった一部とは、エージェントがある作業のために読み込むものです。それは次の和集合です。ブートプロファイルの無条件の読み込み集合と、有効なエージェントプロファイルの requiredRead(エージェントは、適用範囲とは独立にこれを基礎として保持します)。展開されるカテゴリに属するすべての統制された意図の文書。項目 2 によって作業のパスが選ぶすべての統制されたエントリ。項目 8 によってパスで直接選ばれたすべての記録。そして、作業がパスまたは合図となるもしくは展開されるカテゴリで一致するすべての live の決定に、組織全体に及ぶ適用範囲のない live の決定を加えたものです。
合図となるカテゴリが寄与するのは照合だけで、それも決定とフェデレーションのマウントに対してであり、コーパスを展開することは決してありません。
一部はタスクエンベロープではありません。経路を計算するツールは、その一部とともに、エージェントが指示なしに読み込んではならない材料、すなわち記録の候補と、その周りの候補に関するメタデータを返します。エンベロープごと読み込んでしまえば、経路を決めた意味がなくなります。
結果の順序は規範的です。ツールがそれを出力する場合、独立した実装どうしがバイト単位で一致するように、カテゴリはこの仕様の正典のカテゴリ順、文書と記録と決定はパスの昇順、マウントは名前の昇順です。文字列の比較は UTF-8 上のバイト単位であり、ロケールやコードポイントの照合順に依存しません。
ツールは、パスの集合からこの一部を計算する補助機能を提供しても構いません(MAY)。リファレンス実装はそれを公開しています(route)。そうした補助機能は適用範囲に依存する部分を計算するものであり、呼び出す側がすでに持っている基礎を出力する必要はありません。その基礎を読み込むエージェントの義務は変わりません。素の読み手がこのアルゴリズムに従うなら、いつでも経路の決定は適合しています。
補足(非規範的)#
現在、リファレンスのツールは変更履歴のスキーマと追記のみの規律を確認します(leji validate が両方を実行します)。基準リビジョンに照らした変更履歴の網羅性、すなわち変更されたすべての統制対象パスが追加エントリに含まれることの検証は、報告用チェックとしてロードマップにありますが、現時点では処理を阻止するゲートではありません。それが提供されるまでは、プロセスとして申告されるレビューと CI の規律によって網羅性を担保します(conformance.md を参照)。
インデックスは、統制されたコンテキストをたどるためのナビゲーションの出典です。leji viewer は、統制された中核部分をインデックスから描画し、その下に、閲覧可能な参照領域としてリポジトリ自体のディレクトリツリーを表示します。これにより、ひとつのビューで、統制されたコンテキストとチーム既存のナビゲーションの両方を確認できます。どのドキュメントツールでも、同様にインデックスを投影できます。表示方法は非規範的です。このサーフェスは意図的に小さく設計されています。5 つの形があれば、ツールがコンテキストレイヤーを検証し、差分を取り、鮮度を判定して、エージェントを適切な一部へ導くのに十分です。同時に、チームがサーフェス全体を把握できる程度に少なく抑えられています。この 5 つを超えるものは 1.0 より後の領域であり、実際の運用から必要性が示されるのを待ちます。
決定
決定記録は、なぜそうしたのかを日付付きで説明する、コンテキストレイヤーの文書です。アーキテクチャ上の決定、ベンダーの選定、適用範囲の境界、意図的に決定しなかったことなどを記録します。議論の蒸し返しを防ぎ、エージェントに規則だけでなく、その理由も伝えます。
決定記録は、正式な記録の下位種です(content-categories.md の「意図と記録」を参照)。本質的に記録であり、一般の記録にはない統一スキーマとライフサイクルを持ちます。生成されたインデックスのエントリには kind: record が含まれます。決定記録自体が kind キーを宣言することはありません(スキーマは閉じているため、明示的な kind は検証に失敗します)。
要件#
- 決定記録は、
decision-record.schema.jsonに照らして妥当な YAML フロントマターを持つ markdown で、1 ファイルにつき 1 件です。決定のまとまりは、マニフェストが宣言する 2 つのサーフェスの和であり、コンテキストレイヤーはそのどちらか一方でも両方でも使って構いません(MAY)。宣言された記録のパス(machine.decisionRecordsPath、既定は<root>/decisions/)と、decisionsカテゴリのインデックスファイルが解決するエントリです。記録は、少なくとも一方から到達できなければなりません(MUST)。 - フロントマターは次を持たなければなりません(MUST)。
id(安定したもの)、title、status、date。statusはproposed、accepted、superseded、deprecated、rejectedのいずれかです。 - 本文は散文で、文脈(どんな状況が決定を迫ったのか)、決定そのもの、そしてその帰結を述べなければなりません(MUST)。推奨される節の見出しは
## Context、## Decision、## Consequencesです。記録は## Alternativesを加えても構いません(MAY)。 - 記録は追記のみの履歴です。記録を編集して別の決定に作り変えてはなりません(MUST NOT)。決定が年を重ねるにつれて変わりうるフロントマターのフィールドは 2 つ、
status(そのライフサイクル)とsupersededBy(取って代わられたときに設定されます)だけです。それ以外、すなわちid、当初のtitleとdate、宣言された適用範囲、そして散文の本文は、いったん公開されたら不変です。撤回や変更は、フロントマターにsupersedesを設定した新しい記録であり、古い記録のstatusはsupersededになってsupersededByが設定されます。取って代わりのリンクは、双方向で一貫していなければなりません(MUST)。記録 B がsupersedes: Aを設定したとき、記録 A はstatus: supersededとsupersededBy: Bを持ちます。またsupersededの記録は、その後継をsupersededByで名指ししなければなりません(MUST)。どちらの記録も残ります。リファレンスのツールは現時点では、この双方向の取って代わりの一貫性を強制します。不変性そのもの(公開済み記録の凍結されたフィールドと本文が、基準となるリビジョンに照らして変わっていないこと)は、まだ検証しません。それは報告として行うチェックとしてロードマップにあり、まだ阻止するゲートではありません。それが出るまで、不変性はプロセスとして申告されるレビューの規律に委ねられます(conformance.md を参照)。 - 記録は
affectedPathsとaffectedCategoriesを宣言しても構いません(MAY)。これによりツールは、作業の適用範囲から、それを統制する決定へ経路を定められます。作業の適用範囲がどのように記録を選ぶか(重なりを踏まえたパスの包含、狭いカテゴリの照合、accepted/deprecatedの拘束、そしてどちらも宣言していない記録の組織全体としての扱い)は、machine-readable-surface.md の Task routing のアルゴリズムです。 - 却下された提案も記録です(
status: rejected)。採用しなかった決定を書き残すことは、最も低コストで議論の蒸し返しを防ぐ方法です。
ADR との互換性(非規範的)#
Leji の決定記録は、意図的に Architecture Decision Records と互換性を持たせています。既存の ADR ディレクトリでは、各記録(または今後作成する記録)にフロントマターのフィールドを追加し、マニフェストでそのディレクトリを割り当てれば、decisions の要件を満たせます。ADR ツールは必須ではありませんが、使用を妨げるものでもありません。
ガバナンス
コンテキストレイヤーとウィキを分けるものが、ガバナンスです。その基本モデルは輪です。アクセスは平等でも、権限は平等ではありません。
輪を規範として述べる#
- 全員が読みます。コンテキストレイヤーにアクセスできるすべての参加者は、人もエージェントも同じく、その全体を読めなければなりません(MUST)。内部でロールによって読み取りを区切ったコンテキストレイヤーは、共有コンテキストレイヤーではありません。人によって読める資料が異なる場合、その資料は別々のコンテキストレイヤーに属します(下のアクセスの境界と distribution.md を参照)。
- 誰でも提案します。参加者は誰でも、人でもエージェントでも、コンテキストレイヤーへの変更を提案して構いません(MAY)。エージェントが書いた提案は一級です。作業中に足りないコンテキストや間違ったコンテキストを見つけたエージェントは、それを表に出した作業と同じ変更一式の中で修正を提案すべきです(SHOULD)。提案は、レビュアーがその意図と見込まれる効果を理解できるだけの理由を伴うべきです(SHOULD)。その理由が、人が承認するために最低限必要なものです。Leji 1.0 は、一般化された証拠のプロトコルを定義しません(1.0 の適用範囲を参照)。
- 承認するのは人です。コンテキストレイヤーへのすべての変更は、正典になる前に人によって承認されなければなりません(MUST)。承認は、リポジトリの既存のレビューの仕組み(プルリクエスト)に乗ります。Leji は別のプロセスを持ち込みません。参加はどのインターフェースを通じて行われても構いませんが(MAY)、正典の承認は、その仕組みの中の監査可能なレビュー記録でなければなりません(MUST)。承認した人に帰属し、レビュー対象の変更一式に結び付いたものです。外部の議論、チャット、チケットの状態、文書のコメントの中だけで表明された承認は、そうした記録になるまでは数えられず、それをコメントへ写しても同じです。人の関わり方を広げても、権限が記録される場所が動くことはありません。
要件#
- 著者ではなく所有者。マニフェストは主たる所有者(
owners.primary)を名指ししなければならず(MUST)、継続性の所有者(owners.continuity)を名指ししても構いません(MAY)。後者は、主たる所有者が不在のときや離れたときに同じ説明責任を担う別の人です。支援を受けた導入では、外部の助けが去る前に継続性の所有者を名指しすべきです(SHOULD)。一人で運用するコンテキストレイヤーには、いなくても構いません(MAY)。それは、承継がないことを正直に示します。所有者は説明責任を負う人です。エージェントは提案しレビューしますが、所有することは決してなく、主たる所有者を継続性の所有者として再び挙げても何の備えにもなりません。所有者が説明責任を負うのは、コンテキストレイヤーの健全さです。それがいまのものであり続けること、古くなった内容や矛盾する内容が刈り取られること、そしてどの領域にも手入れをする人がいることです。所有者はキュレーターではありません。内容は、輪の全員が仕事をしながら書き、真であり続けさせるものです。それをひとりの番人に集めることこそ、このモデルが避けようとしているボトルネックです。 - レビューの範囲。コンテキストレイヤーの変更は、影響を受ける内容にもっとも近い人たち、すなわち領域の所有者にレビューされるべきであり(SHOULD)、ひとりの門番に集約されるべきではありません。領域の所有は、リポジトリの既存の所有の対応表(
CODEOWNERSファイル、チームの慣習)であって、マニフェストの新しいフィールドではありません。Leji は、承認にプルリクエストを再利用するのと同じように、これを再利用します。すべての領域に所有者がいるようにする説明責任は、主たる所有者にあります。レビューが問うのは「これは真か」だけではありません。なぜこれがコンテキストレイヤーに属するのか、誰がこれを頼りにするのか、それが成り立つことを何が示すのか、いつ見直すべきか。これらに答えられない変更は、リンクかメモであって、正典のコンテキストではありません。あらゆる変更一式について常に立てるべき問いは、この変更はコンテキストを変えたかです。答えが「はい」なら、コンテキストの差分は同じ変更一式に属します。 - 収録と削除。提案は開かれていますが、収録はそうではありません。内容がコンテキストレイヤーに属するのは、それが今後の仕事のやり方を変える場合だけです。すなわち、制約を定める、決定を記す、インターフェースや所有の境界を定義する、繰り返される間違いを止める、のいずれかです。それ以外はリンクされるのであって、取り込まれません。コンテキストレイヤーは、承認の道筋と同じくらい意図的な削除の道筋を持たなければなりません(MUST)。古くなった内容、取って代わられた内容、重複した内容は、通常のレビューされた変更一式の中で刈り取られ、刈り取りは別立ての掃除プロジェクトではなく、各領域の所有者の務めの一部です。増えるだけのコンテキストレイヤーは、レビューを通りながら腐っていくものです。持続する指針は、二度実証のゲートを通るべきです(SHOULD。content-categories.md に従います)。一度きりの修正はマージして構いませんが、規範が正典になるのは、それが少なくとも 2 つの実際の作業で持ちこたえたあとです。望んでいることではなく、真であることを書き留めてください。
- 変更履歴の規律。
indexed以上の適合性では、承認されたコンテキストレイヤーの変更ごとに、machine-readable-surface.md に従って機械可読な変更履歴のエントリを追加しなければなりません(MUST)。 - 鮮度。鮮度は意図のための仕組みです。意図の文書とエージェントプロファイルは、レビューの期限(インデックスエントリとプロファイルの
freshness.reviewAfter)を持つべきであり(SHOULD)、記録は持ちません(その日付がいつ時点の記録かを示します。content-categories.md に従います。記録に宣言された期限は検証エラーです)。ツールは、期限の過ぎた意図の内容を報告すべきであり(SHOULD)、古くなった内容を黙っていまのものとして扱ってはなりません(MUST NOT)。作業のためにコンテキストを読み込む読み手は、読み込んだもののうちレビューの期限が過ぎているものを、その作業の出力の中で示さなければなりません(MUST)。古さが埋もれるのではなく、人に見えるようにするためです。運用上の系列において次に来るはずの記録が遅れているかどうかは、これとは別の概念(ストリームの新しさ)です。1.0 はそれに名前を与えるだけで、仕組みは定義しません。作業の必須のコンテキストとは、ブートプロファイルの無条件の読み込み集合と、有効なエージェントプロファイルのrequiredReadと、Task routing のアルゴリズムがその作業のために選ぶ一部(経路に載った live の決定と、経路に載った統制された意図の文書、加えて作業のパスが直接選んだ記録。machine-readable-surface.md に従います)の和です。必須の項目の期限が切れているとき、読み手はそれに基づいて先へ進むのではなく、停止するか尋ねなければなりません(MUST)。レビュー期限を過ぎていると分かっているコンテキストに基づいて動くことは、この規則が防ごうとしている、静かな古さの失敗そのものです。必須でない古い項目は、その古さを注記したうえで使って構いません(MAY)。governedの適合性では、鮮度の期限は宣言され、確認されなければなりません(MUST。適合性のチェックリストに従い、報告だけの確認でも構いません)。そのチェックを CI で実行することが推奨されます。レビューの鮮度(上記)は、チェックアウトの現在性、すなわち読み手が持っている写しが正典のリポジトリと一致しているかどうかとは別のものです。読み手は、チェックアウトの現在性をバージョン管理システム(git)から確かめます。ワーキングツリーは、チェックアウトしたリビジョンの時点でのみいまのものであり、ツールが未検証の写しを黙っていまのものとして扱ってはなりません(MUST NOT)。アクセス可能な git のワーキングツリーもバージョンのメタデータもない、ただのファイル内容としてコンテキストレイヤーに到達した読み手(リポジトリを伴わずに別のインターフェースへアップロードまたは同期されたファイル内容)は、チェックアウトの現在性を、いまのものとしてではなく未知として扱わなければなりません(MUST)。 - 正典の内容はコンテキストレイヤーの中にあります。仕事のやり方を統制する知識が、ベンダーの設定ファイル、チャットのスレッド、個人のメモの中にだけ存在してはなりません(MUST NOT)。仕事を統制するのであれば、それはレビューの下、コンテキストレイヤーに属します。
アクセスの境界#
Leji は、自前のアクセス制御の仕組みを定義しません。コンテキストレイヤーへのアクセスは、バージョン管理システム(git)と、リポジトリが置かれているプラットフォームによって統制されます。すなわち、リポジトリホストの権限と、ワーキングツリーを見せているファイルシステムまたは共有ドライブです。アクセスの単位はコンテキストレイヤーです。
- コンテキストレイヤーは、アクセス制御されたリポジトリに置かれても構いません(MAY)。Leji はそのアクセスを与えも、確認も、強制もしません。それを行うのはバージョン管理システムとそのホストです。
- 適合するコンテキストレイヤーは、その内部でロールによって区切られた読み取りを要求してはなりません(MUST NOT)。「全員が読む」の範囲は、そのコンテキストレイヤーの読み手に限られます。バージョン管理システムが受け入れた全員が、そのコンテキストレイヤーの全体を読みます。
- より狭い読み手を必要とする内容(経営、財務、セキュリティ、インシデント対応の文脈)は、独自のリポジトリ、マニフェスト、所有者、レビューのゲートを持つ別のコンテキストレイヤーに置かれなければならず(MUST)、その権限はバージョン管理システムが与えます。制限された文脈は別のコンテキストレイヤーであって、共有されたレイヤーの中の制限された区画では決してありません。
- 制限されたレイヤーを別のチームのコンテキストに組み込むのはフェデレーションの場合であり、制限付きマウントについての追加の規則が distribution.md にあります。
保守のモデル(非規範的)#
コンテキストレイヤーは、日々の仕事に組み込まれ、差分ごとに保守されます。作業の中で不足や誤りのあるコンテキストが明らかになり、その修正が同じレビュー対象の変更一式に含まれ、変更履歴に記録されます。ドキュメント専用のスプリントは必要なく、今後も必要ありません。誤ったコンテキストは、その日のうちに誰かが問題を実感するような誤出力を生みます。これにレビューと、仕組みを確認する CI を加えたものが、強制の仕組みの全体です。
この速いフィードバックにより、誤った内容はすぐに検出されます。一方、時間をかけて蓄積する凡庸または冗長な内容は、上記の収録基準と削除の経路に基づき、各領域の所有者が見つけます。これはキュレーションであり、Leji では意図的に分散させています。一人のキュレーターに任せる方法は、品質を保つ安全策に見えて、実際には逆効果です。その人がシステム内で最も遅い経路となり、変更が滞留するか、その人を迂回するようになります。しかも、その人が持つコンテキストは領域の専門家より少ないものです。その結果、コンテキストレイヤーは停滞するか、分裂します。各領域の所有者が担当範囲を整理し、収録基準を守ることで、コンテキストレイヤー全体を小さく正確に保ち、ボトルネックを避けられます。所有者はシステムの健全性を管理し、輪の参加者は内容を管理します。
配布
コンテキストレイヤーを、それが説明する仕事に対してどこへ配置するかを定めます。3 つのパターンがあり、すべてに共通する規則がひとつあります。コンテキストレイヤーはドキュメント専用であり、利用するどのリポジトリにも、ビルド時や実行時の依存を持ち込んではなりません(MUST NOT)。
パターン 1: モノレポ(既定)#
コンテキストレイヤーは、それが説明するコードやインフラと同じリポジトリの、コンテキストルートに置かれます。チームの仕事がひとつのリポジトリにある場合、これが推奨されるパターンです。コード、インフラ、コンテキストが一緒にバージョン管理され、構造上ずれが生じにくくなります。
パターン 2: 複数リポジトリ構成のための、ドキュメント専用のサブモジュール#
仕事が多数のリポジトリにまたがる場合、コンテキストレイヤーは専用のコンテキストリポジトリに置かれ、利用側のリポジトリがそれを git サブモジュールとしてマウントします。
- コンテキストリポジトリは、自身の
leji.json、ブランチのポリシー、レビューのゲートを持つ通常の git リポジトリです。 - 利用側のリポジトリは、それを固定のパス(推奨は
context/)にマウントしなければならず(MUST)、その存在にビルドや実行時の手順を結び付けてはなりません(MUST NOT)。マウントが欠けていたり古かったりすると知識が落ちるだけで、ビルドが落ちることは決してありません。 - 各利用側リポジトリは、コンテキストレイヤーの特定のバージョンをピン留めします。ピンの更新は、レビュー可能な変更一式として届かなければなりません(MUST。スクリプトやボットが上げるプルリクエスト)。こうして、コンテキストの変更がリポジトリごとに見え、レビューでき、帰属が分かるようになります。
- ツールは、古いピンを報告すべきです(SHOULD。各利用側リポジトリがコンテキストレイヤーからどれだけ遅れているか)。古いピンの報告は、いかなる阻止的な強制よりも先に来なければなりません(MUST)。まず可視化、ゲートはあとです。1.0 のリファレンス SDK のピンの報告が扱うのはフェデレーションのマウント(パターン 3)です。このパターンの利用側のピンについてはチェックを同梱していないので、
federatedではこの項目はプロセスとして申告されます(conformance.md を参照)。リファレンスのチェックが出るまでは、チームまたはチーム自身のツールがそれを報告します。
パターン 3: 兄弟コンテキストレイヤーのフェデレーション#
パターン 1 と 2 は、どちらもコンテキストレイヤーがひとつです。モノレポはひとつを所有し、複数リポジトリの組織はひとつを利用します。フェデレーションは、すでに複数のチームがそれぞれ自分のコンテキストレイヤーを所有している組織のためのパターンであり、目的は、誰も支配権を手放さずに、それらのコンテキストレイヤーを互いに読めるようにすることです。
まず思いつくのは、すべてを統合する方法です。ひとつのコンテキストリポジトリに、各チームの知識を集めます。しかし、この方法は避けてください。コンテキストレイヤーが最新に保たれるのは、所有者が作業のたびに読み、誤りを同じ変更一式で修正するからです。プロダクトのコンテキストをプラットフォームのリポジトリへ移すと、プロダクトの内容と、それに対する説明責任が切り離されます。誰もが「今は別の誰かが所有している」と思い込むうちに、内容は劣化します。知識の中央集約は、もともと知識を個人の頭やスレッド内に閉じ込めていたボトルネックを再現することになります。
フェデレーションは、コンテキストレイヤーを吸収せずに合成します。あるチームのコンテキストレイヤーは、別のチームのグラフへ兄弟として加わります。マウントされ、参照され、読まれますが、コピーされることはありません。
-
兄弟コンテキストレイヤーは、ホストのマニフェストの
federation.mountsで宣言されるピン留めされたマウントとして加わります。兄弟のnameとowner、そのsourceとなるリポジトリの所在、そしてホストが読む兄弟のリビジョンの、不変なコミット id を完全な形で示すpinです。ピンはそのマウントのバージョンの記録であり、マニフェスト自体に保持されるので、ピンの更新はレビュー可能な変更一式として届きます。マウントが記録するのは、このリポジトリが別のチームの真実のどのバージョンを読んでいたかであって、そのフォークではありません。マウントはtrackingRefを宣言しても構いません(MAY)。それは出典側の完全修飾のブランチまたはタグで、古さと到達可能性はそれに照らして判断されます。ない場合は、確認の時点で出典が提示している既定ブランチが使われ、報告の中で名指しされます。 -
兄弟は、それを生かしているすべてを保ちます。自分のリポジトリ、所有者、レビューのゲート、変更履歴、適合性の宣言です。ホストは、兄弟の内容を自分の中へコピーしてはなりません(MUST NOT)。所有するチームから切り離された内容は、誰も責任を負わないまま古くなります。それこそが、フェデレーションが防ごうとしている失敗そのものです。
-
マウントされた内容は、リゾルバがハイドレートするレイヤーの投影として実体化されるのであって、コミットされた写しとしてではありません。兄弟はたいてい、より大きなリポジトリの中に埋め込まれたレイヤーなので(パターン 1)、兄弟をまるごとチェックアウトすると、そのコンテキストを読むために製品を取り込んでしまいます。そこでツールは、ピンの時点でのレイヤーの投影、すなわち兄弟自身のマニフェストが読み取り可能にしているすべての重複を除いた和(ルートの
leji.json、宣言されたコンテキストルート下のツリー、ブートプロファイル、ピンの時点で存在する場合の機械可読なインデックスと変更履歴のファイル、存在する場合のエージェントプロファイルと決定記録のツリー、agentsのバインディングが名指しするすべてのエージェントプロファイル、すべてのカテゴリインデックスファイル、そしてピン留めされた生成済みコンテキストインデックスが列挙するすべての統制されたパス。それらがどこにあっても含まれます。投影を定義するのは兄弟自身のマニフェストであり、ホストがそれを取捨することはありません)を、ホストのバージョン管理が無視する一時的なキャッシュへ取り出します。失敗の境界も同じ線に沿います。ピンの時点で参照されているかスキーマが要求するファイル(ブートプロファイル、カテゴリインデックス、結び付いたエージェントプロファイル、インデックスされた統制されたパス)が欠けている場合、投影は失敗し、宣言している成果物と欠けているパスを名指しする安定したコードを返します。一方、ディレクトリが存在しない場合や機械可読な成果物が存在しない場合は、その実効的な場所が宣言されたものでも既定のものでも、何も寄与せず、何も失敗させません。git は空のディレクトリを表現できませんし、生成されたインデックスを持たないレイヤーは、ルートのツリーを超える内容の閉包を単に持たないだけです。可用性の種類の投影の失敗(ピンの内容が欠けているか壊れている)は、そのマシンでそのマウントを利用不能にするだけで、通常のホストの検証やホストの製品ビルドを失敗させることは決してありません。安全性または内部の種類の投影の失敗(外へ逃げるパス、壊れた文字列、限度の超過)は、ハイドレーションをゼロでない終了コードで中止し、途中までの投影が公開されることは決してありません。ピン留めされたバイト列は、git のオブジェクトストア(マシンごとのヒントとなるリポジトリ、リゾルバが管理するストア、ホストのサブモジュールのオブジェクトデータベース)から解決されるのであって、いかなるワーキングツリーからでもありません。またネットワークへのアクセスは、明示的で同意された手順としてのみ行われます。ホストのリポジトリが、自分の都合で兄弟のサブモジュールを持っていても構いません(MAY)。ツールはそれをローカルのオブジェクトストアのひとつとしてのみ扱い、チェックアウトが読み取り対象のマウントされた内容になることは決してありません。キャッシュの中身も含め、兄弟の内容をホストへコミットすることは適合しません。パターン 2 のドキュメント専用の規則は、構造上そのまま成り立ちます。ホストの中で、投影に対してビルドしたり実行したりするものは何もありません。リポジトリルートに解決される machine のパスは、何も選びません。宣言された
machine.agentProfilesPathやmachine.decisionRecordsPathが兄弟のリポジトリルートに解決される場合、それは投影に対してディレクトリの選択を寄与しません。それを尊重すれば兄弟のリポジトリ全体を取り込むことになり、レイヤーの投影はまさにその結果を避けるために存在するからです。参照されているものが失われることはありません。agentsのバインディングやピン留めされた生成済みインデックスを通じて個別に名指しされたプロファイルと決定記録は、なお運ばれます。落ちるのは、ルートを一括で選ぶことだけです。投影の限度。リゾルバは 4 つの上限を強制しなければなりません(MUST)。独立した実装が、それぞれ自分の天井を選ぶのではなく、同じ入力を同じように拒むためです。投影が運ぶエントリは、重複を除いたあとで最大 65,536 件です。その内容の合計は最大 2 GiB(2,147,483,648 バイト)です。投影される単一のパスは 4,096 バイトを超えません。これは、文字数でもコードポイント数でも、実装ごとに異なるランタイム固有の文字列の単位でもなく、そのパスの UTF-8 表現として測られます(そうしなければ、実装ごとに受け入れるパスが変わってしまいます)。ツリー全体の一覧 1 回分は、転送上で最大 256 MiB(268,435,456 バイト)です。これは、リゾルバが選択のために読む列挙のメタデータを制限するものであって、バイト数の上限が抑える投影される内容そのものではありません。この 2 つが意図して別の数字なのは、大きなリポジトリが、まったく小さく妥当な投影を持ちうるからです。この 4 つのいずれを超えても、安全性の種類の投影の失敗になります。
-
実体化されていないマウントは、知識を落とすだけで、ビルドを落とすことは決してありません。検証は 3 つの関心事を分けます。嘘をついているマニフェストはエラーです。マウント名の重複、ホスト自身の
nameを再利用したマウント、sourceやpinの欠落や不正がそれにあたります。宣言されたマウントが単にこのマシンでハイドレートされていないだけなら警告です。正直に低下した可用性として、報告され、飛ばされます。実体化された投影のピンに対する完全性は、ツールが示す診断であり、明示的に選んだ強制の下でのみ致命的になります。通常の検証は、マウントが利用できないことを理由に失敗したり、取得したり、確認を求めたりしてはなりません(MUST NOT)。強制を望むホストは明示的にそれを選びます(フェデレーションの健全性チェックは、ハイドレートしたうえで可用性を要求しても構いません(MAY))。作業に必須のマウントが利用できない場合の読み手の義務は、下のフェイルクローズの規則です。 -
マウントは読み取りを可能にするのであって、権限を与えるものではなく、アクセスを与えるものでもありません。兄弟をマウントするホストは、すでにその兄弟へのアクセスを持っている読み手やエージェントを、そこへ導きます。マウントは、そのアクセスを与えるものでも、兄弟の変更を承認するものでもありません。各コンテキストレイヤーへの書き込みは、依然としてその所有者が承認し、誰がそれを読めるかは、依然としてバージョン管理システムが決めます。フェデレーションは、関係するリポジトリがすでに受け入れている参加者のために、読めるコンテキストを合成します。誰が承認するか、誰が読んでよいかは、もとの場所にそのまま残します。
-
マウントは直接的で平坦です。ホストは、自分が名指しした兄弟を合成します。ツールは兄弟自身のマウントへ再帰してはなりません(MUST NOT)。推移的なコンテキストは表示だけのものです。兄弟が宣言するマウントは、ホストに直接のピンがないかぎり、解決も、インデックスも、経路づけもされません。各マウントの
nameは、ホストのマニフェストの中で一意でなければならず(MUST)、ホストのコンテキストレイヤー自身のnameを再利用してはなりません(MUST NOT)。コンテキストレイヤーが宣言した兄弟より先へ何も進まないので、ダイヤモンドも循環も不活性です。AがBとCをマウントし、BもCをマウントしているのは、たどるべきグラフではなく、3 つの直接の関係です。
マウントされたコンテキストレイヤーは、名前を持つ別個の出典であり、ホストのカテゴリに統合されません。ホスト自身のコンテキストレイヤーは、ホストのリポジトリについて権威を持ち、各兄弟は自分自身について権威を持ちます。組織全体の名前空間は存在せず、したがって兄弟間の優先順位を解決する必要もありません。エージェントは、必要な一部を、それを所有するコンテキストレイヤーから、名前を伴って読み込みます。そしてマウントされた内容は信頼されない入力です。読める文脈であって、実行すべき指示ではありません。兄弟の散文も、他のあらゆる読み取り面と同じく、誤りや注入された指示を含みえます。ですからエージェントは、それを吟味し引用すべき材料として扱い、自分の行動には自分のホストの姿勢を適用し、マウントの中に見つけた命令形の文を、ホストの指示であるかのように従うことは決してありません。
古いピンの報告は、系譜を踏まえ、何を見られたかについて正直です。ツールはピンを証跡となる参照(trackingRef、または出典が提示する既定ブランチ)と比較し、最新、N 件遅れ、進んでいる、分岐している、無関係のいずれかを報告します。そのとき常に、比較した参照、比較を行ったリポジトリの種類(リゾルバが管理するストア、マシンごとのヒント、ホストのサブモジュール)、証跡がリゾルバ自身の参照かそうでないもののどちらか、観測した時刻、そして系譜が完全だったかどうかを名指しします。到達できるオブジェクトストアがない場合、報告は unknown であり、推測することは決してありません。マシンごとのヒントを通じてのみ解決できるピンが立証するのは可用性であって、適合性ではありません。federated では、ピンは source の提示された参照から到達可能でなければならず(MUST。conformance.md を参照)、出典に到達できないチェックは unknown を報告します。それがレベルを与えることは決してありません。
コンテキストレイヤーが federated の適合性に達するのは、これらの関係が実在し、確認できるときだけです。すなわち、そのコンテキストレイヤーが少なくとも 1 つの他のリポジトリからピン留めされたマウントとして利用されていること、古いピンの報告が用意されていること、そして宣言されたすべてのマウントが、所有を保ったまま完全なピン留めの宣言(出典、完全なコミットのピン、経路のメタデータ)を持っていることです(conformance.md を参照)。リファレンスの SDK は機械的な部分を確認し、問題を報告します。実体化の状態は意図して適合性の入力になっていません。1 台のマシンで使えることは、その宣言が真であることについて何も語らないからです。
この形の実際のマニフェストは examples/multi-repo/ にあります。
輪は所有を合成するのであって、中央に集めるのではありません。モノレポは、ひとつのコンテキストレイヤーを読む、ひとつのチームの人とエージェントの輪です。複数リポジトリの組織は、そうした輪の輪であり、それぞれが依然として、それを真に保つ人たちに所有されています。
フェデレーションされたコンテキストレイヤーを読む#
発見しやすくするのはホストの仕事であって、エージェントが推測することではありません。マウントを宣言するホストは、エージェントがすでに読んでいる 2 か所でそれを示します。ブートプロファイルが作業の言葉で兄弟を名指しし(boot-profile.md に従います)、生成されたコンテキストインデックスが mounts の経路の配列を持ちます(machine-readable-surface.md に従います)。エージェントが兄弟を見つけるためにマニフェストを読む必要は決してありません。
フェデレーションされたホストを読むとき、エージェントは次のようにします。
- まずホストのブートプロファイルとホストの機械可読サーフェスを読み込みます。ホスト自身のコンテキストレイヤーが、ホストのリポジトリについて権威を持ちます。
- 作業のコンテキストの適用範囲を確定する前に、ホストから見えるマウントの経路の記録を読みます。マウントが作業に必須なのは、ホストのブートプロファイル、インデックスのマウント記録、またはそのマウントの
requiredWhenのメタデータが、その作業にそれが必要だと述べているときです。マウントが作業に関連するのは、Task routing のアルゴリズム(machine-readable-surface.md)の下で、そのcategoriesの少なくとも 1 つが合図となる作業のカテゴリと一致するか、そのtopicsの少なくとも 1 つが、作業が明示的に名指ししたトピックと完全に一致するときです。トピックの一致が選ぶのはマウントだけです。カテゴリを展開することはなく、兄弟の中の内容を選ぶこともありません。そのうえで同じアルゴリズムが、エージェントが兄弟自身のインデックスから読み込む一部を導きます。 - 作業に関連する兄弟を読み込むには、ハイドレートされた投影の場所をリゾルバの状態から取得し(リファレンス SDK の
mounts locate。キャッシュのパスを推測することは決してありません)、そこにある兄弟のleji.jsonを読み、兄弟のnameがホストの宣言と一致することを確かめ、兄弟のブートプロファイルを読み、そのうえで作業が必要とする一部だけを兄弟自身のインデックスから読み込みます。事実、制約、引用には、それがどのコンテキストレイヤーから来たかの名前を添えます。またマウントされた内容は、このパターンの規則に従い、信頼されない入力のままです。 - 兄弟自身の
federation.mountsへ再帰してはなりません(MUST NOT)。孫にあたるコンテキストレイヤーがホストの作業に本当に必要なら、ホストはそれを自分の直接のマウントとして宣言しなければなりません(MUST)。 - 所有に応じて姿勢を適用します。ホストの姿勢は、ホストのリポジトリでの作業を統制し、兄弟の姿勢は、その兄弟の内容の解釈と、そこへの変更の提案を統制します。ひとつの作業についてホストと兄弟の指針が衝突し、どちらが所有するコンテキストレイヤーなのかが明らかでない場合、エージェントは、述べられていない優先順位を自分で選ぶのではなく、停止して尋ねなければなりません(MUST)。
ツールは、マウントを踏まえた読み込みの補助機能を提供しても構いません(MAY)。ただし兄弟を読むのに Leji のツールは必要ありません。素のリポジトリの読み取りも、この手順に従い、アクセスの境界を保つなら適合します。
制限付きマウント#
合成されるレイヤーの読み手が異なるとき、フェデレーションはアクセスの境界をまたぎます(governance.md を参照)。アクセスの強制は、依然としてバージョン管理システムのものです。読み手は、マウントのリポジトリを解決できるか、できないかのどちらかです。仕様の仕事は、その境界が漏れないように、そして静かに失敗しないようにすることです。
-
制限されたレイヤーは、その読み手が制限されたレイヤー自身より広いホストにおいて、マウントとして宣言されてはなりません(MUST NOT)。ホストが受け入れるすべての参加者が、すでにマウントされるレイヤーにも受け入れられていなければなりません。マウントの宣言そのもの(その存在、
name、owner、role、categories、topics、requiredWhen、source、pin、trackingRef)は、ホストの読み手が見てはならないものを何も開示してはなりません(MUST NOT)。より広い読み手が制限された決定を必要とする場合は、制限されたレイヤーへのマウントではなく、墨消しされたコンパニオンレイヤーか、公開できる決定の要約を出してください。 -
マウントは参照であって、アクセスの付与ではありません。マウントを宣言しても、バージョン管理システムがすでに許している範囲を超えて、そのレイヤーを読める人が広がることは決してありません。ある読み手がそれを解決できるかどうかは、ホストのマニフェストではなく、そこで決まります。
-
フェイルクローズで、決して黙って進まない。作業に必須のマウント(フェデレーションされたコンテキストレイヤーを読むで定義)を解決できない読み手は、停止して、コンテキストが不完全であることを報告しなければなりません(MUST)。到達できないレイヤーが存在しないかのように進めてはなりません(MUST NOT)。見えていない部分的なコンテキストに基づいてエージェントが動くことこそ、この規則が防ごうとしている失敗です。逆もまた失敗です。作業に必須または作業に関連する兄弟を解決できるのにそれを飛ばす読み手は、黙って不完全なコンテキストに基づいて動いており、適合しません。
ここでツールが裏づけられることとできないこと。バリデーターはローカルでの可用性(宣言されたマウントで、ここにハイドレートされた投影がないもの)を報告し、経路のアルゴリズムはカテゴリとトピックの重なりによって作業への関連を判断します(リファレンスの SDK は作業に関連するマウントを示します。machine-readable-surface.md の Task routing を参照)。しかし作業に必須かどうかは
requiredWhenによって決まり、それは自由記述の作業条件です。また実行時の到達可能性は、読む時点での読み手自身のアクセスによって決まります。どちらもエージェントが判断すべきことであって、ツールが判断することではありません。したがってフェイルクローズの MUST は、エージェントが申告するものです。ツールは見えるものを示し、エージェントが停止を守ります。
補足(非規範的)#
サブモジュールの評判が悪いのは、ビルドと結合したコード用サブモジュールの問題によるものです。ドキュメント専用の末端には、そうした失敗要因がありません。これを対象にコンパイルされるものはなく、更新が遅れても壊れるものはありません。ピンは単に、「このリポジトリが真実のどのバージョンを基に作業していたか」を記録します。これはリスクではなく、情報です。
フェデレーションは統合より構成要素が多く見えますが、実際には少なく済みます。統合は最初だけ低コストで、その後は継続的に高いコストがかかります。以降、チームをまたぐ編集はすべて中央リポジトリの所有者を経由し、どのチームも日常的に読まない部分から劣化していきます。兄弟としてマウントすれば、各コンテキストレイヤーを小さく保ち、所有者がいて、実際に読まれる状態を維持できます。必要なのはピンの更新だけです。会議ではなく、レビュー可能な差分として扱えます。
適合性
段階的に導入できるよう設計されています。4 つのレベルがあり、それぞれが前のレベルを含みます。チームは自分たちのレベルをマニフェスト(conformance.claimedLevel)で宣言します。自己申告のみで、認証プログラムはありません。
適合性は、チェックを実行する場所に実体化されたコンテキストレイヤーに照らして評価されます。その写しが表している可能性のある、正典のレイヤーに照らして評価するのではありません。リポジトリを伴わずに取得した写しは、context-layer.md の縮退モードで読まれます。縮退した読み取りが正典の権威へ至る経路になることはありません。このような写しは検証されず、ツールは判断を保留せず、その事実を明示します。
チェックリストの大半の項目は機械で検証されます。リファレンスのツールがレイヤーに照らして確認し、成立しない宣言を失敗として扱います。報告される結果は 4 種類であり、意図的に互いを置き換えられないようになっています。
fail: 証拠を収集した結果、要件が満たされていません。- (プロセスとして申告)、
manualとして報告されるもの: チームの実践(レビューのゲート、CI のジョブ、外部の利用者)に関する項目で、ツールがリポジトリだけから確認できないため、チームが保証します。この形で報告されるのは、以下で**(プロセスとして申告)**と付された項目だけです。 unknown: 機械で検証される項目のうち、その実行では証拠が得られなかったもの。たとえば、出典へのアクセスなしでのフェデレーションのピンの到達可能性のチェックや、比較すべき git の基準がない状態での追記のみの規律です。unknownがレベルを与えることは決してなく、証拠を伴う実行なら確認できたはずの宣言を否定することも決してありません。not applicable: このレイヤーには当てはまらない条件付きの機械項目。たとえば、マウントを宣言していないレイヤーにおけるフェデレーションのマウントの項目です。採点されず、どちらの方向の証拠にもなりません。
ツールが報告する verifiedLevel は、当てはまる機械で検証される項目がすべて通る最も高いレベルであり、そのレイヤーが宣言しているレベルを上回ることは決してありません。fail も unknown も、レベルの付与を妨げます。プロセスとして申告される項目と当てはまらない項目は採点されません。宣言による上限は意図的です。検証が答えるのは、その宣言が成り立つかどうかであって、そのレイヤーが何を宣言できたかではありません。ですから core を宣言しているレイヤーは、証拠上は governed に届くとしても core と報告され、報告されるレベルを上げる方法は、宣言を上げることです。verifiedLevel がプロセスとして申告される項目を主張することは決してないので、verifiedLevel が通っていることは、それらを含むレベルにとって必要条件ではあっても十分条件ではありません。以下の各項目は、**(プロセスとして申告)**と付されていないかぎり、機械で検証されます。
機械で検証される項目のうち 2 つは、縮退した写しにおいて異なる振る舞いをします。その違いは、それぞれが持つ証拠から導かれます。git の存在には答えが出ます。git リポジトリの中にない写しは、コンテキストレイヤーが git リポジトリの中にあるという core の要件を満たさないので、その項目は fail です。変更履歴の追記のみの規律には答えが出ません。ファイル自体は完全に整っているかもしれませんが、比較に必要な直前のコミット済みの状態に到達できないので、その項目は unknown であり、その写しからは indexed で検証されないというだけです。どちらも manual として報告されることはありません。それは、付されたプロセス申告の項目のために取ってあります。なお、鮮度についての読み手の規則(読み込んだ古いコンテキストを示し、期限切れの必須項目では停止するか尋ねること。governance.md に従います)は読み手の振る舞いであって、適合性のゲートではありません。リファレンスの leji route は、経路に載った各文書にレビューの期限と失効の有無を刻むので、エージェントはそれを適用できます。
3 つの項目は、現時点では述べられた意図ほど深くは検証されておらず、その隔たりは読者が自分で見つけるのではなく、ここで名指しされています。ブートプロファイルの項目は、宣言されたパスに存在すること、そして identity、loading、posture の見出しを備えていることとして検証されます(見出しが欠けていれば validate は毎回 boot-profile-sections の警告として報告しますが、ゲートにはなりません)。Identity 節が実質的なことを述べているかどうかは、任意で有効にする --content のリントに委ねられます。このリントは、プロファイルのどこに残っていてもプレースホルダーのテキストを指摘します。実在する決定の項目は、解決された記録の少なくとも 1 件にスキーマ上妥当なフロントマターがあることとして検証されます。本文の中身(雛形ではなく実際の決定であること)も同じく --content で検査されます。3 つ目が変更履歴の項目です。追記のみの規律は HEAD におけるファイルの状態に照らして確認されるので、ワーキングツリーにまだある書き換え、つまり pre-commit フックが存在する理由となっている場合を捕まえます。継続的インテグレーションのチェックアウトでは、ワーキングツリーが HEAD なので、すでにコミットされた状態で届いた書き換えはこのチェックからは見えず、それを覆うのは変更一式のレビューです。したがってこの項目が検証するのはワーキングツリーであって、履歴ではありません。この 3 つの項目に述べられた意図は、適合するコンテキストレイヤーが備えるものとして規範的であり続けます。機械によるチェックを深めることと、変更履歴を明示的な基準リビジョンに照らして比較することは、リファレンスのツールのロードマップにあります。federated の検証は、さらに、宣言された federation.mounts のエントリが少なくとも 1 件あることを必要とします。提供する側だけのコンテキストレイヤー(他のリポジトリから利用されてはいるが、自らはマウントを宣言していないもの)は governed で検証され、そのフェデレーションにおける立場は、プロセスとして申告される利用の項目に委ねられます。
レベル 1: core#
コンテキストレイヤーが存在し、人もエージェントもそこから仕事ができます。
- コンテキストレイヤーが git リポジトリの中にあり、それが説明する仕事と一緒にバージョン管理されている(context-layer.md の要件に従います)。
- リポジトリルートに
leji.jsonがあり、マニフェストのスキーマに照らして妥当である。 - 宣言されたパスにブートプロファイルがあり、identity、loading、posture を扱っている。
- 少なくとも
domainかsystemが(そのインデックスファイルを通じて)割り当てられ、解決される意図の文書を少なくとも 1 件含んでいる(記録だけでは運用上のコンテキストにならないため)。加えてdecisionsに少なくとも 1 件の実在する決定記録がある。すなわち、具体的なstatusと、本文に実際の決定を持つ記録であって、空の雛形やプレースホルダーではないもの。 - 名前の分かる主たる所有者がいる。
- ベンダーのエントリポイントファイルは、存在する場合、ブートプロファイルへ誘導している。
レベル 2: indexed#
コンテキストレイヤーがツールから読めるようになっています。
-
coreのすべて。 - 生成されたコンテキストインデックスがあり、ツリーと一致している。
- 機械可読な変更履歴があり、コンテキストレイヤーの変更がエントリを追加している。
レベル 3: governed#
強制力が善意ではなく仕組みになっています。
-
indexedのすべて。 - コンテキストレイヤーの変更がリポジトリのレビューのゲートを通り、人が承認している。(プロセスとして申告)
- エージェントプロファイル(少なくとも core のプロファイル)が、プロファイルのスキーマに照らして妥当である。
- CI がサーフェスを検証している。マニフェスト、ツリーと一致するインデックス、変更履歴の規律、プロファイルのフロントマター、宣言されたパスが解決されること。(プロセスとして申告)
- 鮮度の期限が宣言され、確認されている(報告だけでも構いません)。
レベル 4: federated#
コンテキストレイヤーが、複数リポジトリの組織にまたがっています。
-
governedのすべて。 - コンテキストレイヤーが、少なくとも 1 つの他のリポジトリからピン留めされたマウントとして利用されており、ピンの更新がレビュー可能な変更一式として届いている。(プロセスとして申告)
- 古いピンの報告が用意されている。利用側が、自分のピンが証跡となる参照からどれだけ遅れているかを見られること。リファレンスの SDK の系譜を踏まえた報告は、宣言されたフェデレーションのマウントを扱います。それを超える利用側の報告はチームのものです。(プロセスとして申告)
- 兄弟のコンテキストレイヤーがあれば、distribution.md に従う完全なピン留めされたマウントとして宣言されている。正規化された
sourceと、完全なコミットのpinがあり、所有が保たれていること。どれか 1 台のマシンでの実体化の状態は、適合性の入力ではありません。 - 宣言された各マウントのピンが、その
sourceの提示された参照(宣言されたtrackingRef、または出典の既定ブランチ)から到達可能である。このチェックには出典へのアクセスが必要です。それがなければ結果はunknownであり、unknownがレベルを与えることは決してありません。マシンごとのヒントを通じてのみ解決できるピンは可用性であって、適合性ではありません。 - 宣言された各マウントが経路のメタデータを持っている。少なくとも
categories、加えてtopicsかrequiredWhen。エージェントが兄弟を読まずに関連を判断できるようにするためです。 - ブートプロファイルがマウントされたすべての兄弟を示し、生成されたインデックスが
mountsの経路の配列を持っている。エージェントがマニフェストを読まずに兄弟を見つけて読み込めるようにするためです(boot-profile.md、machine-readable-surface.md に従います)。
補足(非規範的)#
core は、コンテキストレイヤーを実体のあるものにするための最低限のレベルです。indexed では、ツールが読む生成済みサーフェスを追加します。governed は、コンテキストレイヤーの保守を誰かの規律に頼らなくなる段階です。federated は、独立性を保つ価値のあるコンテキストレイヤーを、すでに複数のチームが所有している組織向けです。たいていのチームは governed まで進み、そこで止めるべきです。federated はそのような組織のためのものであり、成熟度を示すバッジではありません。
バージョニング
仕様、スキーマ、それらを実装するツールの 3 つは、それぞれ独立してバージョン付けされます。
仕様#
- 仕様は SemVer のバージョンを持ちます(現在は 1.0.0)。互換性を壊す変更にはメジャーバージョンが必要です。すべての変更はリポジトリの変更履歴に記録されます。
- コンテキストレイヤーは、対象とする仕様系列を
leji.jsonの中で、自らを名乗るlejiキーによって宣言します(たとえば"leji": "1.0")。これは OpenAPI の慣習に倣ったものです。値は仕様の系列(major.minor)であって、仕様のパッチバージョンではありません。パッチのリリース(1.0.0から1.0.1)は、系列を動かさずに文言やツールを整えるものなので、マニフェストはどのパッチをまたいでも"1.0"のままです。ツールは、最新の系列ではなく、宣言された系列に照らしてコンテキストレイヤーを検証しなければなりません(MUST)。
プレビュー系列#
仕様系列はプレビューとして指定されても構いません(MAY)。プレビュー系列はその場で改訂できます。すなわち、一般提供(GA)で凍結されるまでは、新しいバージョンへ上げるのではなく、本来なら互換性を壊すような形で変わっても構いません(MAY)。「互換性を壊す変更にはメジャーバージョンが必要」という規則(項目 1)と、「互換性のない形の変更では $id の系列が変わる」という規則(項目 3)が適用されるのは GA での凍結以降であって、系列がプレビューの間ではありません。GA で系列は凍結され、両方の規則が有効になります。
一般提供の前に出る系列は、その最初のリリースでそのことを宣言しなければなりません(MUST)。
1.0 系列は、v1.3.0 のリファレンスツールのリリースをもって凍結されています。この系列では、スキーマの変更は追加に限られ、$id は v1.0 のままです。互換性のない変更は必ず新しい系列として公開され、既存系列をそのまま置き換えることはありません。
スキーマ#
- 各スキーマは、
https://leji.org/schemas/v<major>.<minor>/<name>.schema.jsonという形の安定した$idを持ちます。$idの系列が変わるのは、そのスキーマの形が互換性のない形で変わったときだけです。 - 公開された系列の中では、スキーマの変更は追加でなければなりません(MUST。新しい任意のフィールド)。フィールドの削除や意味の変更には、新しい系列が必要です。
- マニフェスト以外の機械可読な成果物は、それが書かれた対象のスキーマ系列を
schemaVersionで宣言します。マニフェストは、自らを名乗るlejiキーで対象の仕様系列を宣言します(項目 2)。
安定集合#
次の要素は、仕様系列の中で凍結されます。ツール(将来の商用実装を含む)は、別途並行するスキーマを用意せず、これらを対象に実装できます。
- マニフェストの形と、その固定のファイル名
leji.json - カテゴリ識別子(
domain、system、practice、governance、decisions) - 適合レベルの識別子(
core、indexed、governed、federated) - machine-readable-surface.md に従う識別子とパスの正規化の規則
- インデックスエントリ、変更履歴のエントリ、エージェントプロファイル、決定記録の各形
実装するツール(非規範的)#
SDK と CLI はそれぞれ独自の SemVer でバージョン付けされ、対応する仕様系列を宣言します。このリポジトリのリファレンス SDK は、npm パッケージの @leji-org/leji(packages/sdk)、PyPI パッケージの leji(packages/sdk-py)、Go モジュールの leji(packages/sdk-go、単一の静的バイナリ)です。いずれも同一の振る舞いをし、ひとつの共有フィクスチャ一式でテストされています。