


裏で動くサービス(デーモン)を使うアプリのコードを、小さく直す
コマンドで動かすツールとデスクトップアプリが、同じ裏のサービス(デーモン)を使うプロジェクトで、AIにコードを書かせる時に使う。先に資料を読み、プロジェクトのチェックを回し、ネットワークとデータの決まりを守り、課題ごとに1つの変更の申し出(プルリクエスト)を出す。
このAIルールを使うには
## コードを変える前に、設計の資料を読み、フォルダごとの役割をつかむ
変更は小さく保ち、リポジトリが現在目指しているアーキテクチャに合わせ、置き換えが進行中の古い実装から受け継いだ挙動よりも、文書化されたデーモン/APIの境界を優先する。
構成はファイル名ではなく役割で理解する。
- バックエンドには、コマンドラインクライアント、ループバックHTTPデーモン、各サービス、組み込みデータベースのストレージ、ライフサイクル/リーパーのロジック、ランタイムおよびワークスペースのアダプター、そしてそれらのテストが含まれる。
- デスクトップまたはウェブのフロントエンドは、生成された型付きクライアントを介してデーモンに接続された、薄いスーパーバイザーかつUI表面である。デーモンのロジックをそこへ移してはならない。
- docsディレクトリには、現行のアーキテクチャとステータスのノートが置かれている。ライフサイクル、クライアント、エージェント、ストレージ、デーモンの挙動を変更する前に読むこと。
- 別のテストディレクトリには、新規インストール時のコンテナチェックを含む、外部のスモークテストおよびエンドツーエンドの資産が置かれている。
- CIワークフローの定義が、何が通らなければならないかの基準である。可能な限り、それらのコマンドをローカルでも同じように実行する。
些細でない変更の前には、次の順序で読む。クイックスタートのREADME、docsのインデックス、アーキテクチャ文書(メンタルモデル、パッケージ構成、ライフサイクル/セッション/サービスの境界、そして根幹を支えるルール)、ステータス文書(今日のmainブランチで出荷済みのものと、まだ進行中のものの違い)、そしてクライアント文書(意図された形)。リポジトリが他のエージェントツール向けの互換用ポインタファイルも抱えている場合は、それらを別個のルールセットではなく、唯一の正典ガイドへのリダイレクトとして扱う。
エントリーポイントは闇雲に検索するのではなく責務から特定する。コマンドの実装、HTTPコントローラーとDTO、サービスの読み書きの境界、ドメインの語彙、ポートの契約、データベースのクエリとマイグレーション、そして生成されたデータベースコードは、それぞれ自身のパッケージに存在する。新しいスタイルを持ち込む前に、近くにあるコマンドやテストのパターンに従う。
## プロジェクトのチェックを回し、画面の変化は実際に表示して見る
その場しのぎのコマンドを考案するのではなく、リポジトリ自身の集約コマンドを使う。リポジトリのルートからは、通常これらが対象になる。lint(バックエンドのテストに加えて、バージョンを固定したリンター)、フロントエンドの型チェック、クエリとスキーマからのデータベースアクセスコードの再生成、API仕様とフロントエンドの型の再生成、そしてCI定義を検証するローカルのワークフローランナー(コンテナのソケットが必要になる場合がある)。
コンポーネント単位のチェックも、反復作業の最中に直接実行する価値がある。バックエンドではビルド、テスト、レース検出を有効にしたテスト、静的解析(vet)、そしてサービスの起動。フロントエンドでは型チェックとビルド。
変更がUI上で目に見えるものである場合は、プロジェクトのプレビューコマンドを使ってセッション内で実際にレンダリングし、その結果を見ること。変更がどう見えるはずかを説明するだけで済ませない。
## アプリの入れ方は、自動で更新されるデスクトップ版だけを案内する
プロジェクトに複数のインストール経路がある場合、正典はひとつだけであり、案内はそこを指し示さなければならない。
- リリースを通じて公開される自動更新のデスクトップビルドが、正典のインストール経路である。ユーザーにはまずそこを案内する。
- 旧来のパッケージマネージャー経由のチャネルは、もはや推奨されない状態のまま、まだ機能していることがある。最終バージョンが公開されてパッケージが凍結されている場合、それ以上の更新は行われない。それは、すでにそのコマンドをPATH上に持っているユーザーにとっての入口として残っているだけであり、起動すると正典のビルドを取得して開く。
- 凍結された旧チャネルを、その製品をインストールする意図された方法として扱うような機能、ドキュメント、フローを追加してはならない。
## 頼まれた事だけを変え、今のコードの分け方に従う
- すべての変更をタスクに直接結びつけて保つ。タスクが明示的に求めていない限り、ついでの整理、広範なリネーム、フォーマットの揺り戻し、投機的な抽象化、アーキテクチャのリファクタリングは避ける。
- 既存のパッケージ境界に従う。クライアントのコードは共有クライアントヘルパーを通じてデーモンのHTTPルートを呼び出す。データベースを開いたり、ランタイムを起動したり、アダプターを直接呼び出したりしてはならない。
- 各コマンドは該当するコマンドファイルの中に置き、近隣のコマンドテストのスタイルに倣ってテーブルテストを書く。
- そのパッケージですでに確立されている依存関係でない限り、サーバーのコントローラーパッケージをクライアントのコードへインポートするのではなく、リクエストとレスポンスのDTOをクライアント側にミラーする。
- 使用方法の誤りは専用の usage error 型を通じて返し、クライアントの誤用が独自のステータスコード(例えば2)で終了するようにする。一方、ランタイムやデーモンの失敗は一般的な失敗コード(例えば1)で終了する。
- デーモンのエラーを表面化させるときは、プレーンな文字列に潰さず、APIのエラーエンベロープとリクエストIDを保持する。
- I/Oやブロッキングを伴う処理を行う関数には、キャンセル/期限のコンテキストを第1引数として渡す。
- 一度きりのユースケースのために抽象化を追加しない。実際の呼び出し箇所をまたぐ重複を取り除ける場合にのみ、ヘルパーを導入する。
- テストは、ユーザーから見える挙動と、変更される境界をカバーすべきである。ハッピーパス、バリデーションと引数の欠落、デーモンのエラーエンベロープ、そして破壊的な操作の確認パスがあればそれも含む。
## デーモンへのネットワークの入口を、変えたり増やしたりしない
- デーモンの主リスナーは、ループバックアドレスにバインドされたまま、認証なしのままにする。そのバインドホストを変更してはならず、そこに認証を追加してもならない。
- デーモンは、ローカルネットワークからのアクセスのために全インターフェースにバインドする、オプトイン方式の第2のリスナーを走らせてもよい。ただし、明示的に有効化されている間だけ、ベアラーパスワード認証のミドルウェアの背後でだけ、そしてアプリケーションAPIだけを提供する場合に限る。そのリスナーは、シャットダウン、テレメトリー、リモート制御といった、ループバックで守られた制御ルートを決して提供してはならない。
- そのようなリスナーが平文であり、信頼されたホームネットワークのみを想定している場合、それは意図的な決定であり、アーキテクチャ決定記録に文書として残されている。そこに手を触れる前に、その記録を読むこと。
- 他のネットワークに面したバインドを追加してはならない。
- コマンドラインクライアントは薄いクライアントのままにする。デーモンのHTTPルートを迂回する古いインプロセスの挙動を移植してはならない。
## 保存しているデータを守り、自動で作られたデータベースのコードを手で直さない
- 導出された状態や表示向けのステータスを保存してはならない。ステータスは、サービスの読み取り時に、耐久性のある事実(アクティビティの状態、終了フラグ、プルリクエスト/チェック/コメントの事実)から導出する。
- ランタイムのプローブが失敗した、あるいは不明であることを、セッションが死んでいる証拠として扱ってはならない。
- 未コミットの変更がまだ残っている登録済みワークツリーを、強制削除してはならない。
- すでにマージされたマイグレーションを変更してはならない。代わりに新しいマイグレーションを追加する。
- 生成されたデータベースコードを手で編集してはならない。クエリまたはマイグレーションを変更し、ジェネレーターを再実行する。
- 変更イベントは、データベースのトリガーから変更ログへ流れる。アーキテクチャが明示的に変更されない限り、ストアのメソッドから変更を手動で発行する並行経路を追加してはならない。
- 生成されたAPIの成果物と、手書きのDTOとの間のずれに注意する。コントローラーのレスポンスの形はひとつのDTOファイルにまとまっており、テストがクライアント層とHTTP層の間のワイヤー互換性を検証していることがある。
- そのパッケージにすでに統合テストやエンドツーエンドのパターンがある場合を除き、テストにネットワーク呼び出しを追加してはならない。インプロセスのテストサーバー、フェイク、注入された依存関係を優先する。
- ローカルの実行状態、デーモンのデータ、一時的なワークツリー、ビルド出力、資格情報をコミットしてはならない。
- アプリケーションのすべての状態を、単一の専用状態ディレクトリの下に保つ。これは文書化された環境変数で上書きできる。デーモンのデータディレクトリ、実行中プロセスのファイル、ワークツリー、そしてデスクトップシェルのユーザーデータ(ブラウザーのキャッシュ、クッキー、ローカルストレージとセッションストレージ、クラッシュダンプ)は、すべてその下に解決されなければならない。オペレーティングシステム既定のアプリケーションデータ格納場所を、読み取りにも書き込みにも決して使ってはならない。デスクトップのエントリーポイントは、シェルのユーザーデータのパスをそのディレクトリに固定している。その上書きを取り除いたり、既定のパスに依存したりしてはならない。
## やり取りの決まり(API)は手で書くファイルで変え、説明書を自動で作り直す
デーモンのAPIはコードファーストである。OpenAPI仕様とフロントエンドの型は生成された成果物なので、出力を編集するのではなく、ソースを編集して再生成する。
- リクエストとレスポンスの形を定義しているDTOファイルを編集する。
- 仕様ビルダーの操作レジストリを編集し、新たに名前が付いた型についてはスキーマ名のエントリーを追加する。
- 仕様の生成と型の生成を順に実行する、リポジトリの集約APIコマンドで再生成する。
- サーバーのHTTPパッケージにあるテストで検証する。それらは仕様のずれと、ルートと仕様の対応をカバーする。生成されたフロントエンドの型はカバーしておらず、そちらは別のCIのずれ検出ジョブが確認する。
- 生成された仕様と、生成されたフロントエンドの型を、ソースの変更と一緒にコミットする。CIは両方を再生成し、コミットされたバージョンが古ければ失敗する。
- 手でミラーしたクライアントのDTOは、意図的な手作業の境界として残っており、生成されるものではない。
## 変更の申し出(プルリクエスト)は、1つの課題につき1つにする
- 既存のプルリクエストを明示的に継続している場合を除き、mainブランチから分岐する。
- 1つのプルリクエストにつき1つの課題に保つ。別の作業を求められたら、別のブランチとプルリクエストを作る。
- feat、fix、docs、test、chore といった、コンベンショナルコミットのメッセージ接頭辞を使う。
- 意図的に省いたものは、プルリクエストの本文で説明する。特に、先行実装が現在の書き直しのドメインより多くの挙動をサポートしていた場合はそうする。
- まず最も範囲の狭い関連テストを実行し、その後に、手を入れた領域に対応するリポジトリのコマンドとCIのコマンドを実行する。
