policy.yaml
policy.yaml の完全なスキーマです。概念 — レイヤー、floor、評価順、信頼 — は
policies ガイド にあります。
ファイル
~/.config/loopkeep/policy.yaml # home layer
<workspace>/.loopkeep/policy.yaml # project layer(commit する)
<workspace>/.loopkeep/policy.local.yaml # project-local layer(gitignore する)
4 つめのレイヤーとして org が評価順に予約されていますが、読み込む経路がないので
現在は効果を持ちません。
完全なスキーマ
version: 1
policies: # 名前付き policy: 再利用できる振る舞いのまとまり
ask-me:
level: ask-first # 必須
timeout: # 任意。waiting 中の run にのみ
after: "72h"
then: abort # abort | deny
rules:
- id: infra-approval # 必須。ファイル内で一意にしておく
policy: ask-me # このファイルで定義した名前付き policy を参照…
# level: ask-first # …もしくはレベルをインラインで持つ — 両方は不可
# timeout: { ... } # インライン形式も timeout を持てる
match:
paths: ["infra/**"] # gitignore 形式の glob、workspace 相対。リスト内は OR
tool: [bash, edit] # リスト内は OR、キーどうしは AND
tags: [migration] # workflow の x-loopkeep tags / harness が割り当てた tag に一致
workflow: [deploy, "release-*"] # run の workflow 名。完全一致 + * glob
command: ["migrate"] # 生コマンドの部分一致、大文字小文字を無視、いずれか一致
allow_override: false # floor ルール: どの layer も緩められない(既定: true)
reason: "Production infra always gets a human"
defaults:
level: notify # どのルールにも一致しないとき。最も厳しく宣言した layer が勝つ
delivery: # 個人 layer のみ。配送を整え、レベルは変えない
digest: { cron: "0 9 * * *" } # システムローカル時刻。これが既定値でもある
quiet_hours: ["23:00-07:00"] # ローカル時刻。notify push と期日済み digest を保留
# 任意、個人 layer のみ: リポジトリの .claude permissions.ask を
# ask-first の引き上げとして取り込むのをオプトアウトする。
import_repo_ask: false
delivery が効くのは home と project-local レイヤーだけです。digest cron と quiet hours は
daemon のマシンのシステムローカル時刻を使い、workflow の on.schedule は UTC を使います。
notify の項目はすぐ受信箱に見え、quiet hours 外なら接続中のデスクトップへ live 更新も届きます。
digest cron を明示しなければ、別のまとめた digest 通知をローカル時刻の毎日 09:00 に送ります。
home と project-local の digest cron は上書きではなく和集合になるため、両方の layer に定義すると
同じ日に 2 回 digest が届くことがあります。
quiet-hours の各エントリは正確な HH:MM-HH:MM で、開始と終了は異なる時刻でなければなりません。
開始を含み、終了は含まず、日付をまたげます。複数のエントリは和集合になり、曜日指定には
対応しません。不正な
エントリは警告して 1 件ずつ無視し、その警告は daemon のログに書かれます。通知配送が保留されて
いても受信箱の項目は見えます。live の notify 更新は window の終了後、順序を保った 1 batch で
流します。期日が来た digest は終了まで workspace ごとに保持・結合し、ask-first は常に即時配送
します。Slack 通知を設定している場合、ask-first は即時、notify はスケジュールされた digest
だけで届きます。
レベル
auto-approve < notify < ask-first < deny。順位が意味を持ちます。floor と
自己申告の引き上げは最大値を取ります。
match の意味論
- paths — gitignore 形式:
**は任意の深さ、*は 1 セグメント内、?は 1 文字、[...]は文字クラス。brace 展開はなし。workspace ルートからの相対で照合。 - tool — アクションのツール名。MCP ツールは
mcp:<server>/<tool>。 - workflow — 完全一致に加え
*glob。評価に workflow 名がないとき (例:--workflowなしのlk policy test)、workflow制約を持つルールは 一致しません。 - command — 部分一致、大文字小文字を無視、いずれかの要素が一致。同じ「文脈なし」 ルール: 生コマンドがなければ一致しません。シェル変数でかわせます — floor をこれに 作らないでください。
- 空・省略した軸は無制約です。存在するすべての軸が一致しなければなりません(AND)。
名前付き policy
ファイルごとに定義します。policy: の参照は同じファイル内でのみ解決されます。
未知の参照はパースエラーです。policy: と level:/timeout: の両方を持つルールは
エラーです — 振る舞いは 1 か所にあります。
ルールの timeout
そのルールで止まった waiting 中の run に適用されます。期限が切れると run は abort
され(または then: に従ってアクションが deny され)、その決定は timeout として
記録されます。timeout がなければ、waiting 中の run はいつまでも待ちます — 見えて
いて、勝手に決められることはありません。
CI で policy をテストする
lk policy test は daemon なしで動き、結果から exit code を決めます —
--expect <level> は 0/1 を、--expect なしはレベルの順位(0–3)を返し、エラーは
exit ≥ 10 です。policy の変更も、ほかのコードと同じようにテストと一緒に出せます。
CLI リファレンス を参照してください。
スターター policy
lk init はこれを書きます:
version: 1
rules:
- id: block-deploy
match:
tags: [deploy]
level: ask-first
allow_override: true
defaults:
level: notify
すべては notify、deploy タグの付いたものは ask first。緩めるにも締めるにも使える、
意図的な出発点であって、推奨ではありません。