ポリシーと信頼
policy はアテンションのルーターです。エージェントが提案するすべてのアクション — 編集、コマンド、MCP のツール呼び出し — は、実行される前に 決定的にあなたの ルールで評価され、4 つのレベルのいずれかに振り分けられます。エージェントが自分の レベルを決めることはありません。
| レベル | 何が起きるか |
|---|---|
auto-approve | あなたなしで実行。通知なし。 |
notify | 実行し、止めずに受信箱を更新する。配送時期はチャネルによる。 |
ask-first | run は止まり、受信箱であなたの決定を待つ。 |
deny | アクションはきっぱり拒否。run は続き、監査記録が残る。 |
上の deny は policy による通常の判定です。監査記録を書き込めないためにアクションを拒否する場合は
別です。アクションは実行せず、run は失敗として終わり、書き込みに失敗した
監査記録は残りません。
policy の置き場所
~/.config/loopkeep/policy.yaml # home — あなた、すべての project で共通
<workspace>/.loopkeep/policy.yaml # project — チーム、commit してレビューする
<workspace>/.loopkeep/policy.local.yaml # project-local — あなた、この project だけ(gitignore する)
レイヤーは意図的に「最も近いものが勝つ」ではありません。2 種類のルールは異なる 振る舞いをします:
- default は出発点です。project-local → home → project の順で解決し、最初に 一致したものが勝ちます — project の default はいつでも自分向けに上書きできます。
- floor(
allow_override: false)は交渉不能の最低ラインです。すべてのレイヤーに またがる、一致するすべての floor が適用され、最も厳しいものが勝ちます。これは 両側に効きます。チームの floor をあなた個人の設定で緩めることはできず、home policy の floor は、信頼していないリポジトリの中でもあなたを守ります。
ルールを書く
ルールはアプリの ポリシー 画面(project のレイヤー)または 個人ポリシー(home の
レイヤー)で編集します。Rules エディタ フォームが 1 項目ずつ組み立て、Raw タブが
ディスク上の policy.yaml そのものを持ち、入力に合わせてキーと値を補完します。ファイルを直接
編集してもかまいません。スキーマはどちらでも同じです:
version: 1
policies:
ask-me: { level: ask-first, timeout: { after: "72h", then: abort } }
rules:
- id: prod-migration
match:
paths: ["migrations/**", "infra/prod/**"] # gitignore 形式の glob、リスト内は OR
tool: [bash, edit] # 異なる match キーは AND
policy: ask-me
allow_override: false
reason: "Production changes always get a human"
defaults:
level: notify
delivery:
digest: { cron: "0 9 * * *" }
quiet_hours: ["23:00-07:00"]
ルールは 5 つの軸で突き合わせます。paths、tool、tags、workflow、そして
command(生のシェルコマンドに対する部分一致)です。ルールはレベルをインライン
(level:)で持つか、名前付き policy を参照します(policy:)— 両方は不可です。
完全なスキーマは policy file リファレンス にあります。
2 つの軸には注意が要ります。tags は workflow の自己申告で、command はシェル変数で
かわせます — 便利なケースのエスカレーションに使い、硬い floor はエージェントが
書き換えられない paths と tool で書いてください。
評価の仕組み
提案された各アクションについて:
- default を解決する — project-local → home → project の順で最初に一致した
default ルール。なければ、どこかのレイヤーが宣言した最も厳しい
defaults.level。 - floor を計算する — すべてのレイヤーで一致する
allow_override: falseの ルールのうち最大のレベル、加えて組み込みの floor。 - 自己申告を適用する — エージェントは自分のアクションを sensitive と申告できます。 これはレベルを 上げる ことしかできず、下げることはできません。
- 最終レベル = この 3 つの最大値。
floor がより低い default を上書きしたとき、その引き上げは監査イベントとして記録され ます — 監督が実際に働いた瞬間が見えます。
組み込みの floor
loopkeep 自身の監督の仕組みへの書き込みは常に ask first で、どのレイヤーもこれを
緩められません。対象は、あなたの loopkeep 設定、workspace の .loopkeep/
ディレクトリ(エージェントが正当に作業する run の worktree は除く)、実行中の
workflow 自身の定義ファイル、loopkeep のバイナリ、そしてイベントログです。floor は
deny ではなく ask-first です — あなたの判断を必ず求めるだけで、アクションを
拒否するわけではありません。シェルコマンドも検査されますが、保護されたパスが
書き込みの位置 に来たときだけです。リダイレクトの宛先か、rm・cp・mv・
tee・dd・sed -i の引数です。保護されたファイルを読むだけ、あるいはコマンドの
どこかに名前が出てくるだけでは floor はかかりません。エージェントが、自分を監督する
policy をこっそり編集することはできません。アプリは、これらの保護対象を
個人ポリシー 画面に読み取り専用で一覧表示します。
loopkeep のツールのうち 4 つは、パスではなくツール名で効く floor を持ちます。
ask_human は ask-first を、notify_human・slack_reply・slack_post は notify を
下回りません。エージェントがあなたに届くための経路は、どのレイヤーの設定でも
塞げないということです。逆にこれより厳しくすることはできます。
これを 2 つの backstop が締めくくります。承認画面は常に生の diff かコマンドを主表示 とし(エージェント自身の要約は副次)、verdict なしに実行されているアクションを 検知したら、run はただちに一時停止します。
信頼
clone したばかりのリポジトリは、緩い policy と前のめりなトリガーを積んでいるかも しれません。そこで policy は direnv のように動きます。workspace を信頼するまで、その project policy と自動トリガーは無効です — あなたの home policy と手動の run だけが 動きます。信頼は policy ファイルのハッシュを記録します。あとで編集があると、あなたが diff を承認するまで workspace は信頼されていない状態に戻ります。あなた個人の floor は、 信頼に関係なくどこでも適用されます。
アプリでは、ポリシー 画面でルールを確認して 保存して信頼(または 信頼し直す、
信頼されていないバナーの ポリシーで信頼する)を選びます。ターミナルからは lk trust。
信頼した workspace の trigger を policy を変えずに止めたいときは lk untrust(アプリの
プロジェクト設定 → すべてのトリガーを停止)。承認は残るので、policy が変わっていなければ
lk trust で再確認なしにそのまま再開できます。
explain と test
すべてのアテンションはその provenance を示します — どのルール、どのレイヤー、どの ファイルの何行目か。同じ仕組みをオンデマンドでも使えます。
ポリシー 画面の Policy を確認 カードは、ツール(と任意の paths・tags)を受け取り、 最終レベルと、どのレイヤーのどのルールが決めたかを表示します。期待するレベルに対して テストすることもできます。
explain は一致した各ルール、そのレイヤーとファイルの場所、上書き可能かどうか、
そして最終レベルを表示します。test は結果から exit code を決めるので、policy の
変更もほかのコードと同じように CI テストに載せられます。
digest と quiet hours
delivery(home と project-local レイヤーのみ)は通知が届く 方法 を整えるもので、
レベル は決して変えません。notify の項目はすぐ受信箱に見えます。接続中のデスクトップには、
quiet hours 外なら live の受信箱更新もすぐ届きます。
delivery.digest.cron は別に 1 つへまとめた digest 通知をスケジュールし、既定は毎日 09:00 です。
この cron と quiet_hours はどちらも daemon のマシンのシステムローカル時刻を使います。
workflow の on.schedule cron は引き続き UTC です。
quiet window は固定幅の HH:MM-HH:MM で、開始と終了は異なる時刻にします。開始を含み、終了は
含みません。日付をまたげ、複数の window は和集合になります。v0.3 では曜日指定はありません。
不正なエントリは警告してその 1 件だけを
無視し、正しいエントリは残します。警告は daemon のログに書かれます。live の notify 更新は window 中
保留し、終了時に順序を保った 1 batch で流します。window 内に期日が来た digest は workspace ごとに
保持・結合し、終了まで待ちます。
ask-first は常にすぐ鳴り、digest を待たず、保留中の notify を早く放出することもありません。
設定 → Slack 通知 で workspace と channel を指定すると、同じゲートから Slack にも送ります。
承認リクエストは個別に即時、notify はスケジュールされた digest だけで届きます。Slack のメッセージは
情報表示であり、承認・拒否は loopkeep から行います。
リポジトリの permission 設定は尊重される
リポジトリの .claude/settings.json があるツールに確認を求めている場合
(permissions.ask)、loopkeep はそれをエスカレーション専用の引き上げとして
ask-first に取り込みます — アクションのレベルを上げることはできても、下げることは
できません。workspace ごと、またはグローバルに、個人レイヤーで
import_repo_ask: false にすればオプトアウトできます。