制作の裏側
1人の開発者と、AI コーディングアシスタントと、機械がチェックする大量のルール。
執筆:Nick
Prison Life は、私が Claude Code(Anthropic のコーディングエージェント)と一緒に書いています。ゲームがどういうものか、どの選択肢を取るかは私が決め、キーボードを叩く作業の多くはアシスタントが担当します。コードを見るのは私とレビュー用の AI エージェントだけなので、自動化に頼っています。ルールは心がけではなく、自動チェックとビルドで強制します。
作業の委任
メインの Claude Code セッションは作業を計画し、タスクトラッカー、設計判断の記録、変更履歴を管理します。自分ではコードを書きません。どんなに小さな変更でも、専門の AI エージェントに渡します。各エージェントには、役割とモデルを定めた定義書があります。
| 役割 | モデルの階層 | 仕事 |
|---|---|---|
| アーキテクチャ | Opus | データモデル、ティック、スレッド、経路探索、設計判断 |
| シミュレーションコード | Opus | シミュレーションの各システム、キャラクター AI、ホットループ |
| パフォーマンス | Opus | プロファイリング、ベンチマークの劣化調査 |
| レビュー(読み取り専用) | Opus | 差分のレビュー。ファイルは編集できない |
| クライアントとツール | Sonnet | Godot クライアント、UI、ツール |
| テスト | Sonnet | 受け入れ基準に沿ったテスト |
| コンテンツデータ | Sonnet | スキーマに沿った JSON 定義 |
| 検索、チェックの実行 | Haiku | コード検索、チェックの実行と失敗の要約 |
難しい仕事は最も強力なモデルに、定型の仕事はより安価なモデルに任せます。安価な AI エージェントがチェックに2回失敗したらタスクは1段上の階層へ移り、最上位でも2回失敗したら作業を止めて私に確認します。
「ダメ」と言うフック
Claude Code のフックは、アシスタントの操作の前後で動くスクリプトです。私の環境では、次の3つのルールを強制しています。
- 赤のまま終わらせない。コードを変更したセッションや AI エージェントが終了しようとすると、フックが高速チェック(ビルドと短時間のテスト)を実行します。失敗すれば、終了は拒否されます。
- メインセッションはコードを編集できない。メインセッションからソース、テスト、コンテンツ、ツールへの書き込みを、フックが拒否します。
- レビュアーは読み取り専用。編集ツールは外されていて、ファイルやプロジェクト履歴を変更するコマンドもフックが拒否します。
これらはミスを防ぐためのもので、意図的な回避を防ぐものではありません。腕のあるシェル操作ですり抜けることはできますし、フックのひとつは AI エージェント内でチェックが赤になったことを検知しても、止めることはできません(メインセッションの停止フックが拾います)。そこは正直に書いておきたいと思います。
記録された判断とチェック
- 自明でない選択はすべて書き残す。タスク、マイルストーン、設計判断は、タスクトラッカーでコードの隣にプレーンテキストとして保管されます。複数のタスクに影響する選択肢から選んだときは、背景と結果を添えて記録します。
- チェックは私のマシンで動く。クラウド CI は使いません。ローカルの1本のスクリプトに4つのモード、
fast、test、bench、fullがあります。スクリプトとフックは、Windows と macOS のどちらでも PowerShell 7 です。 - すべての変更にチェック。ローカルの自動化が、説明が不十分な変更や整形が崩れた変更を弾きます。コードの変更では高速チェックも実行し、メモやウェブサイトだけの変更ではビルドを省きます。
- 基準ファイルは保護。固定した決定論ハッシュ、ゴールデンセーブファイル、ベンチマークのベースラインは、私が明示的に承認したときだけ変わります。黙って変わることはありません。
- プロジェクト自体が唯一の正。ローカルのメモリツールがプロジェクトと過去の会話を検索用に索引化しますが、大事なことは必ずタスク、記録された判断、ドキュメントのいずれかにもなっています。
決定論的コア
シミュレーションは、Godot への参照を一切持たない素の .NET ライブラリの中にあります。Godot クライアントはその状態を読み、コマンドを送るだけです。コアは固定のルールに従います。世界が変わるのはコマンドを通じてのみ、時間は整数のティック、乱択は世界のシード付き生成器からのみ。同じシードと同じコマンドログからは、同じ世界ができなければなりません。これらのルールは、レビューだけでなく、コンパイラとテストで検証されます。方法は開発ログ #1、世界、セーブ、リプレイについては開発ログ #2をご覧ください。
- コマンドが唯一の入口です。それぞれ固定の型 ID とバイナリエンコーディングを持ち、すべてのコマンドが、拒否されたものも含めて結果付きでログに記録されます。
- ティックは固定順の3フェーズで進みます。システムの順序はコード上の1か所で決められており、変更するとすべてのハッシュが変わります。
- 乱数は自作の生成器(SplitMix64 と xoshiro256**)を使います。名前付きストリームは世界のシードから導出され、ワールド状態に保存されます。順序に依存しない処理には、ドメイン、キー、ティックで駆動するステートレスなキー付き生成器を使います。
- 状態ハッシュは、決定論チェック用の、順序に敏感な64ビットのハッシュです。暗号用ではありません。
- 世界は、離散的な階層上に並ぶ 1×1 m のタイルのグリッドです。壁、ドア、床は建設コマンドでのみ変更されます。
- セーブとリプレイは、検証付きの同一ファイルコンテナを共有します。リプレイはシードとコマンドログに途中の確認用ハッシュを加えたもので、再生時にステップごとに照合されます。
- ベンチマークは、ヘッドレスのシナリオランナーで取得します。マシンごとのベースラインと、ティックあたりのアロケーションゼロというゲートがあります。
設計上の目標は、毎秒20ティックで最大1,000人の受刑者と職員です。あくまで目標です。まだキャラクターがいないため、ベンチマークランナーはあっても、その規模では何も計測していません。