検索/AIコーディングエージェントに、リリースを壊さずブランチとコミットの決まりを守らせる

AIコーディングエージェントに、リリースを壊さずブランチとコミットの決まりを守らせる

バージョン管理されたリポジトリでAIコーディングエージェントに作業させる時に使う。ブランチの切り方、テスト、コミットの書き方、一時ファイルの置き場所、どのAIツールでも動くエージェント向け指示の書き方を決め、リリースを壊さないようにする。

このAIルールを使うには
## エージェントの指示ファイルを1つに保ち、リポジトリのどこを直すか確かめる - エージェント向けの指示ファイルは正本を1つだけ保つ。ハーネスが別のファイル名を期待する場合は、内容を複製せずシンボリックリンクで正本を指すようにする。実体ファイルを2つ持つと時間とともに内容が乖離し、別パスを別種のアーティファクトとして扱うバリデーターを壊すことがある。シンボリックリンクはそのまま維持し、通常ファイルへ実体化されないようにする。 - 1つの変更が、配布される製品コンテンツ・カタログやconfigのメタデータ・ビルドやCLIのコードなど、複数のサーフェスに同時にまたがることがある。ある変更を「単なるコード」「単なるドキュメント」と決めつけず、変更した各ファイルがどのサーフェスに属するかを確認し、隣接するサーフェスではなく、その振る舞いを所有するサーフェスを編集する。 - リポジトリ自身の指示ファイルは、そのリポジトリを執筆・作業するためのコンテキストとして扱う。配布物のランタイムで作用すべきルールは、リポジトリの指示ファイルではなく、その配布物自体の中に置く。エンドユーザーは自分自身の指示に対してその配布物を実行するからだ。 ## フィーチャーブランチで作業し、プルリクエストを通してだけマージする - 些細ではない変更には必ずフィーチャーブランチを作る。すでにそのタスクに適したブランチにいる場合はそれを使い続け、明示的に頼まれない限り余計なブランチやワークツリーを増やさない。 - デフォルトブランチへのすべての変更はプルリクエスト経由にする。直接プッシュやマージは検証やリリースチェックを迂回してしまい、後から多段階の復旧が必要になるバージョンのずれを生む。これを回避するために作業でごまかすのではなく、ブランチ保護に強制させる。 - リリースはリリース自動化に任せる。通常業務でリリース管理下のバージョン番号を手動で上げたり、リリースノートを手書きしたりしない。自動化が変更の意図を分類できるよう、Conventional Commitsのタイトルを使う。 ## 変更後に全テストを走らせ、自動テストと評価で見る事を分ける - パース・変換・出力・機械的な規約に影響する変更の後は、必ずフルテストスイートを実行する。ローカルのスイートはCIが実行するものと同じであり、スキップしてよい別のローカル専用レーンは存在しない。 - チェックはその性質で分ける。スキーマやフロントマター、パスの安全性、出力の形、grepできる契約といった決定論的な不変条件は自動化スイートに置き、CIで必ずパスさせる。判断力や抑制のような非決定的な振る舞いは、脆い文字列テストとして偽装するのではなく、評価(evaluation)によって検証する。 - レビュアーがスイートで見落とされていたgrep可能な不変条件を見つけた場合は、新しいスイートを追加するより既存のガードを厳しくすることを優先し、ファイル全体をスナップショットするのではなく、その回帰変更で落ちたはずの最小の反証可能な単位(1つのトークン・パス・フィクスチャ)にピン留めする。 ## 一時ファイルの置き場所を決め、計画書やドキュメントはリポジトリに残す - 使い捨ての一時データは、デフォルトでOSのtempへ送る。OSが回収してくれる実行ごとの一時ディレクトリで、1回消費されたら破棄されるファイル向けだ。 - 実行をまたいで残さなければならない一時データには、ランダムな一時ディレクトリの代わりに、現在のユーザーが所有する固定のプレフィックスを使い、後続の実行がそれを見つけられるようにする。ルートは非公開(所有者のみ)で作成し、それがシンボリックリンクだったり別ユーザー所有だったりする場合は拒否する。後続の実行が意図的に兄弟の最終成果物を列挙する場合を除き、1回の実行につき1ディレクトリを保つ。 - 一時データを作業中のリポジトリに紐づけるのは、その成果物がこのリポジトリやブランチから本当に切り離せない場合、ユーザーが直接手を入れて整理することを意図している場合、あるいはリポジトリ相対のパスそのものが出力の核心である場合に限る。 - 計画書・仕様書・ドキュメント・最終成果物といった永続的な出力は、リポジトリで管理される場所に置き、どちらの一時階層にも置かない。 ## 名前はASCIIだけにし、Markdownの表は縦線(|)で区切る - ファイル名・コマンド名・エージェント名といった識別子はASCIIのみに保つ。コンバーターや正規表現パターンがそれに依存しているためだ。 - Markdownの表はパイプ区切りの行で書き、罫線素片文字は使わない。 - 地の文でUnicodeを使うのは構わない。コードブロックやターミナルの例では、コピーしたコマンドの移植性を保つためにASCIIの矢印(-> や <-)を使う方がよい。 ## コミットのfixとfeatureを、変更の中身で使い分ける - Conventional Commitsのプレフィックスは、ファイルの種類ではなく変更が何をするかで選ぶ。docsやskillsディレクトリ配下にあるコンテンツでも製品コードであることはあり得る。docsプレフィックスは、その唯一の目的がドキュメントであるファイルのために取っておく。 - fixとfeatureのどちらのプレフィックスも当てはまりそうな時は、fixをデフォルトにする。壊れている・欠けている振る舞いを直すことは、たとえコードの追加によって実装されていてもfixである。featureプレフィックスは、何も壊れていない状態でユーザーがそれまでできなかった機能のために取っておく。目安:今日書いた回帰テストがその変更前には失敗していたはずなら、それはfixだ。 - 意味のある範囲でもっとも狭いコンポーネントスコープ(スキル名・モジュール名・エリア名)を含め、プロジェクト全体をスコープにすることは絶対にしない。ユーザーの明示的な確認なしに破壊的変更マーカーを追加しない。ユーザーが望まないかもしれない自動メジャーバージョンアップを引き起こすためだ。 ## 相対パスを使い、配布する各ユニットをどのAIツールでも動くようにする - 配布可能な各ユニットは自己完結させる。参照するのは自分自身のディレクトリツリー内のファイルだけとし、相対パスを使う。兄弟ユニットへ入り込んだり、絶対的なインストールパスをハードコードしたりしない。インストール場所はバージョンごとに異なり予測できず、パッケージングは各ユニットを個別にコピーするからだ。ディレクトリをまたいで依存するのではなく、共有ファイルは各ユニットに複製する。 - skill-directory・session-id・plugin-rootといったホスト固有の変数置換に、値が空や未解決の場合のフォールバックなしで頼らない。これらの多くは特定の1つのハーネスでしか値が入らず、他では黙って空になる。 - 同梱スクリプトを実行するシェルコマンドでは、解決を決定論的にする。ユーザーのプロジェクトディレクトリを基準に解決されてしまう裸の相対パスではなく、同じコマンドの中で絶対的なベースディレクトリをインラインで設定し、そこからスクリプトを参照する(シェルの状態は呼び出しをまたいで保持されない)。 ## エージェント向け指示には確かめられる決まりだけを書き、レビューの指摘は仕分けてから直す - 1行を採用してよいのは、反証可能な制約を述べているか、既知のデフォルトの傾向や観測された近道を打ち消しているか、判断を変えるドメイン知識を与えている場合だけにする。「thoroughであれ」のような曖昧な努力・品質に関する言葉は削り、観測可能なルールに置き換えるか、なければ削除する。すでに単独で成立している指示に、動機づけの説明を付け足さない。 - 指示を繰り返すのは、その配置が発火するかどうかを左右することが実証された、ドリフトが起きるポイントだけにする。本当に必要な重複はパリティテストで保護する。 - レビューフィードバックの反映は重要な改訂として扱う。各項目をChange・Verify・Considerに分類し、Change項目だけを編集する。それぞれをその項目が属する層で、最小の仕組みで直す。その後ブロックを読み直し、変更によって重複・陳腐化したテキストを取り除く。レビュアーの言葉は修正方法についての仮説であって、それに対する権威ではない。 ## プロジェクトの決まりは、指示ファイルを名指しせず読み込み済みの内容から探す - ユニットが、課題管理・コミット形式・lintコマンド・スコープの範囲といったプロジェクトの規約を見つけ出す必要がある場合、どのファイルを開くかではなく、エージェントがすでに読み込んでいるコンテキストの中で何を探すべきかを記述する。読み取り経路では、指示用のドットファイルを名指ししない。ホストはすでにルートの指示ファイルを注入しており、そのファイル名はハーネスごとに異なる。名前付きの指示ファイルを開くようエージェントに伝えることは、プロンプトインジェクション対策がまさに検知する形そのものだ。 - 具体的なファイル名を挙げてよいのは、コンテキスト参照ではニーズを表現できない場合、たとえば規約を対象へ書き戻す時や、新しいサブエージェントが本当に引き継がないコンテンツを読む時だけにする。 - 機能はカテゴリで説明する(「プロジェクトの課題管理ツール、例えばGitHub Issues、Linear、Jiraなど」「それが公開しているどんなインターフェースでも」)。バイナリ・環境変数・コネクタが見当たらないことを、その機能が使えない証拠として扱わない。 ## バージョン番号ではなく中身を比べ、セッションが最新の編集を読んでいるか確かめる - 長時間稼働しているセッションが起動時に読み込んだ内容を編集した後は、そのセッションは編集前のコピーのままだと想定する。キャッシュは読み込み時点の状態を捉えるものであり、リリース内での編集はバージョン番号を変えないため、バージョンが一致していても、そのキャッシュがそのリリース由来であることが証明されるだけで、最新の編集を捉えていることの証明にはならない。 - どちらのコピーが有効かは、バージョン文字列を読むのではなく、読み込まれたファイルの内容を作業中のコピーと比較して確認する。一致していれば稼働中のコピーは最新で信頼でき、異なっていれば、その振る舞いのテストを信頼する前に再起動するか、新規読み込みの経路を使う。 - スクリプト・パーサーなど、テストスイートが実行する機械的なコードパスは、常に最新のソースで動く。この陳腐化の問題を抱えるのは、起動時に読み込まれる指示・地の文の層だけだ。