何を信頼できるか
ハンドブックのどの部分がパースされた事実で、どの部分がモデルの出力か。何があなたのマシンを離れるか。そして、このツールが何を拒否するか。
短いまとめ
| 主張 | 出所 | 間違いうるか? |
|---|---|---|
| このファイルはこのパスに存在する | パーサー | いいえ |
| この関数は 88–104 行にある | パーサー | いいえ |
このシグネチャは send(self, url, data) | パーサー | いいえ |
| この関数はあの関数を呼ぶ | パーサー | full ティアではいいえ。generic ティアではベストエフォート |
| これらの呼び出しは解決できなかった | パーサー | いいえ — 列挙されるのであって、推測されません |
| これらのファイルは読めなかった、またはパースできなかった | パーサー | いいえ — 列挙されるのであって、カバー済みとして数えられません |
| このファイルはこのステージに属する | LLM、機械的に検証 | 判断としては、あり得ます。構造的には、いいえ |
| このファイルの目的は「…」 | LLM | はい — 文章です |
| このサブシステムは「…」のように動く | LLM | はい — 文章です |
| この状態はこれらのステージを流れる | LLM、実在するステージ id 上で | はい。ただしステージ id は実在します |
設計全体が従うルール: エージェントはこの表の上半分でルーティングし、行動する前に実ソース
を読みます。SKILL パッケージは最初の行でそう述べており、そのルーティングプロトコルは
「変更を提案または実施する前に、引用されたすべてのパスで実際のソースを read_file する
こと」で終わります。
何があなたのマシンを離れるか
Phase 1 — 何も。 静的解析は完全にローカルです。ネットワーク呼び出しは行われません。
Phase 2 と 3 は、あなたが設定したエンドポイントにソースファイルの内容を送信します。 それは自分のマシンで動くモデル(vLLM、Ollama、LiteLLM)でも構いません。Handbooks には テレメトリもアナリティクスもなく、デフォルトのエンドポイントも OpenAI のもの — キーは あなたが用意します — 以外にはありません。
--max-chars-per-file <n> は、単一ファイルから送信される量の上限を定めます。
レンダリング、パッケージング、検証はネットワークに決して触れません。apply と
rollback も同様です。
プランナーはソースをローカルで読み、読んだものの抜粋を、生成と同じようにエンドポイント へ送信します。
意図的に拒否されるもの
拒否こそ、このツールの構造を支える部分です。優先順に:
パッチャ
- アンカーが0 回マッチ → 拒否。コードは先へ進んでいます。
- アンカーが2 回以上マッチ → 拒否。曖昧です。
- 「最初のマッチを取る」ことは決してしません。それこそ、パッチが間違った関数に着地する 仕組みです。
- 1 つの失敗は、1 バイトも書き込まれる前に、適用の全体を中止します。
- ソースルートを脱出するパス — ファイルがまだ存在しない場合の、シンボリックリンクされた 親ディレクトリ経由を含む — は拒否されます。
- ロールバックは、パッチの「後」に変更されたファイルを、
--forceを渡さない限り拒否します。
プランナー
- 書き込みツールは存在しません。無効化されているのではなく — 実装されていません。
## Tool resultセクションを捏造する返答は、その末尾にあるどんなプランも含めて丸ごと拒否 されます。そのプランはフィクションから導出されたからです。- 諦めたランは、
plan.mdに謝罪文を書く代わりに、非ゼロで終了します。 - 破滅的な正規表現(
(a+)+、(.*)*)は、ランをハングさせる前に拒否されます。
パイプライン
- アナライザが解決できない呼び出しは
dropped-calls.jsonへ行きます。決して推測されません。 - アナライザが読めない、あるいはパースできないファイルは、理由と共に
scan-coverage.jsonへ行き、scannedFilesからは外されます。空のファイルとして記述されることは決して ありません。部分的にしかパースできなかったファイルは残りますが、それでも列挙されます — その事実は本物ですが不完全であり、どのページがそれに立脚しているかを知っておくべきだから です。 - カード生成に失敗したファイルは空の説明を得ます。捏造された説明では決してなく、
_coverage.jsonに列挙されます。 - ドクターループが提案した構造変更のうち、存在しないステージを名指しするもの、あるいは ファイルを孤児にしてしまうものは、スケルトンに触れる前に拒否されます。
- 返答がパースできないクリティックは
REJECTとして数えられます。
設定
- シークレットは決してコマンドラインフラグにならず、設定ファイルに現れた場合は拒否
されます — 設定ファイルはコミットされるからです。シークレットは 2 つ、
llmApiKey/OPENAI_API_KEYとllmExtraBody/OPENAI_EXTRA_BODYです。後者は自由形式で、すべての リクエストボディにマージされ、しかもゲートウェイは実際にボディでの認証を受け付けるため、 チューニング用のフィールドと認証情報を見分けられるものが何もありません。 llmBaseUrlは意図的に、一律のシークレットではありません。チームがすべてのチェック アウトを 1 つの共有ゲートウェイに向けるのなら、それをコミットする正当な理由があります。 設定ファイルで拒否されるのは、認証情報を埋め込んだ URL(https://user:pass@host/v1) だけです。- 与えられたものの不正な値が、デフォルトへこぼれ落ちることはありません。タイポはエラーです。
- 空の値は未設定として読まれるため、
HANDBOOK_TITLE=が無題のハンドブックを生むことは できません。
ドリフトの検出
SKILL パッケージ内の coverage.json は、パッケージング時に取得されたファイルごとの
コンテンツハッシュを持ちます。
handbook validate --skill skills/myrepo --source ~/code/myrepoこのコマンドは生きたソースを再ハッシュし、それ以降に内容が動いたすべてのファイルを報告
します。これが、エージェントが古い主張に基づいて行動する前に「このページはコードより遅れて
いるかもしれない」と知る方法です — そして、handbook skill に --work と --source を
渡す価値がある理由です。
訂正チャンネル
ハンドブックの主張が実ソースと矛盾するとき、それを利用するエージェントはスキルルートの
corrections.jsonl に 1 行の JSON を追記します:
{
"file": "src/engine.py",
"page": "references/stages/stage-2.md",
"claim": "spin() is defined in src/main.py",
"actual": "spin() is defined in src/engine.py"
}handbook resync --corrections <file> は、そこに名指しされたファイルだけを、たとえ
バイトが一度も変わっていなくても更新します。ソースが否定する主張は、そのファイルを記述し
直すのに十分な理由だからです。
このファイルはスキルルートに置かれ、references/ の下には決して置かれません。プランナーが
あのツリーを読み取り専用でマウントするからです。リビルドは、クリーンをまたいで未処理の
訂正を保持します。
Studio のセキュリティ姿勢
Studio はローカルツールであり、そうでないふりをしません。
- デフォルトで
127.0.0.1にバインドします。 - CSRF ガードはソケットではなく
Hostリクエストヘッダをチェックするため、ループバック のホスト名だけが通ります。 POSTはapplication/jsonを要求します。これが古典的なクロスオリジンフォーム攻撃を 防ぎます。- ソースとハンドブックのファイル配信は、登録されたルートにサンドボックスされます。
コンテナ内では、公開ポートに到達可能であるために 0.0.0.0 にバインドしなければなりません
が、それで話しかけられる相手が広がるわけではありません: LAN の IP やコンテナのホスト名を
名指しするリクエストは、依然として 403 を受け取ります。リモートアクセスは意図的に未実装
の、独立した機能です — 明示的な許可リストが必要になるでしょう。
Handbook が知っていると主張しないこと
コールグラフは、「なぜ」その決定がなされたのか、プロダクトが何のためにあるのか、チームの 規約が何であるのかを教えてくれません。Handbooks はそれらを推測しませんし、するふりもしま せん。文書化するのは構造と挙動です。意図を書き残すのは、依然としてあなたの仕事です。