ワークフロー
workflow は markdown ファイルです。YAML frontmatter が「いつ発火し、何をしてよいか」
を宣言し、本文がエージェントへの自然言語の指示になります。workflow は
<workspace>/.loopkeep/workflows/*.md に置かれ、ほかのファイルと同じように
project と一緒にバージョン管理されます。
---
on:
schedule:
- cron: "0 3 * * *"
---
Review yesterday's commits and update CHANGELOG.md with anything user-visible.
{{ trigger }}
形式は GitHub Agentic Workflows と互換です。上流に存在するキーは
上流の意味を保ち、loopkeep は新しいキー(ローカルのトリガー、ローカルの
safe-outputs)を追加するだけです。loopkeep 固有の設定は x-loopkeep の下にネストして
置きます。未知のキーは警告付きで保持され、捨てられることはありません。
すべてのキーは workflow frontmatter リファレンス に記載しています。
編集と実行
アプリで ワークフロー を開きます。+ 新規 workflow は、トリガー、プロンプトの
本文、設定(engine、concurrency、budget、dispatch、launch、coalesce、Slack の
safe-output)のフォームと、markdown ファイル全体を扱う Raw タブを持つエディタを開きます —
編集したブロックの外側のコメントやキーは
保持されます。Edit は既存の workflow を同じエディタで開きます。▶ 実行 は手動 run を
開始し(manual トリガーの無い workflow はヒントを出します)、workflow ごとのスイッチが
自動トリガーをオン/オフします。これらは project が信頼されていない間でも動きます。
Raw タブはただのテキスト欄ではありません。入力に合わせて frontmatter のキーと値、本文の
{{ trigger.* }}、そして連携済みアカウントから取得した名簿(repos / channels / labels /
workflows)を補完します。
トリガー context
何が発火させたのかを構造化して記述したもの — トリガーの種別、ソースイベント、上流の出力 — を
本文から使えます。全体を入れるなら {{ trigger }}、1 つの値だけなら
{{ trigger.event.branch }} のように書きます。書かなければ何も足されません(context の無い
本文になります)。これによって run-completed の workflow は上流の run の出力を見られますし、
Slack で発火した workflow は自分をメンションしたメッセージを見られます。
gateway が locator を渡した場合、対応する GitHub・Slack の発火には trigger.resource として入り、
GitHub の issue・pull request・comment、または Slack メッセージを指す座標になります。無ければ
そのパスは空の値に展開されます。Slack の channel ID が C で始まる run では、本文に正確な
{{ trigger.thread }} を書くと、daemon がエージェントを起動する前に現在のスレッドをローカルで
取得します。判定するのは現在の公開状態ではなく ID の接頭辞です。公開として作成されたチャンネルは
あとで private に変えても C ID のままで、private として作成されたチャンネルの ID と DM の ID では
空の値になります。取得した本文は信頼できない入力として扱ってください。resource の形と遅延取得の規則は
トリガー を参照してください。
engine
engine: { id: claude, model: claude-sonnet-5 }
loopkeep が動かせるのは今は claude エンジンだけで、Claude Code CLI をサブプロセス
として動かします。engine を省略しても run は claude で動きます(gh-aw の既定は
copilot ですが loopkeep はそれを動かさないので、notice がログに出ます)。それ以外の
engine id は run 開始時に失敗し、黙ってフォールバックはしません。model と params
は workflow ごとなので、軽い heartbeat の workflow は小さいモデルで動かし、重い作業は
大きいモデルで動かせます。gh-aw が定義しない engine id を使うなら x-loopkeep の下に
engine を書きます — loopkeep は root の engine よりこちらを優先します(詳しくは
frontmatter リファレンス)。
safe-outputs
safe-outputs は、workflow が生み出してよい効果を宣言します:
safe-outputs:
create-pull-request: {} # GitHub ネイティブ、あなたの gh 認証を使う
local-commit: {} # run の worktree で commit する
notify: {} # inbox に通知を投稿する
slack-post:
channel: C012345 # 必須。エージェントは別の宛先を選べない
workspace: T012345 # 複数候補から選ぶときは必須。1 件に解決できるときだけ省略可
slack-reply: {} # run を起こした Slack の会話にだけ返信
dispatch-workflow:
allowed: [incident-triage] # 別の workflow を発火(明示的な allowlist が必須)
emit-output: {} # chaining 用の構造化 JSON 出力
safe-output を宣言しても監督は迂回されません。エージェントが提案するすべての
アクションは、実行前になお policy 評価 を通ります。emit-output
だけが例外で、これは下流の workflow 向けのデータを生むだけなので常に auto-approve
されます。
Slack の呼び出しは宣言でゲートされます。slack_post は固定宛先を示す
slack-post.channel が無いと使えず、slack_reply は Slack で発火した run でも
slack-reply: {} が無いと使えません。どちらも組み込みの notify floor から始まり、policy で
ask-first まで引き上げられます。
どちらの Slack 出力にも、このデバイスと Console のペアリングが必要です。ペアリングされていない
デバイスでは両方とも deny になります。slack_post には Slack 接続も必要です。workspace を省略した
ときにコネクタ一覧を取得できなければ、宛先を推測せず deny します。
投稿を試みたあとに Slack から失敗が確定した場合も、timeout で成否不明になった場合も、run は 失敗させず受信箱にアテンションを作ります。timeout ではすでに届いている可能性があるため loopkeep は 再試行せず、重複投稿を避けるためエージェントも再試行してはいけません。
workflow を連鎖する
run どうしが直接やり取りすることはありません。3 つの経路がつなぎます:
- run の出力 — エージェントが
emit-outputで JSON を出し、run-completedで購読している workflow がそれをフィルタでき(if: event.output.severity == "high")、トリガー context として受け取ります。 - dispatch — エージェントが
dispatch-workflowで別の workflow を発火させると 決め、入力の payload を渡します。対象はallowedリストに載っている必要があり、 dispatch 自体も policy 評価を受けます。 - ファイルシステム — ファイルを書き、
file-watchのトリガーで下流の作業を 起こします。多段のパイプライン(PDF を置く → 抽出 → 要約)に最適です。
連鎖は暴走しないよう深さで制限されます — トリガー を参照してください。
予算
x-loopkeep:
budget:
tokens: 200000
time_ms: 900000
steps: 30
daily_runs: 300
daily_tokens: 500000
値はすべて整数です。予算を超えた run は failed になります(理由は budget exceeded)。
あなたの承認を待っている時間は time 予算には数えられません。
並列数
x-loopkeep:
concurrency:
max: 1
on_limit: skip # queue(既定)| skip | replace
queue は空きが出るまで新しい run を保留し、skip は発火を捨て(黙ってではなく
記録される)、replace は実行中のものを中断して新しいものを優先します — 最新だけが
意味を持つ heartbeat に便利です。すべての workflow を通じた daemon 全体の上限は
lk global-max で設定します。
tag
x-loopkeep:
tags: [deploy, migration]
tag は policy が突き合わせられる意味的なラベルです。workflow が自己申告するものなので、
エスカレーションの便利手段として扱ってください — 硬い安全ルールは、workflow が
偽れない paths と tool で突き合わせるべきです。