Workflow frontmatter
Every key a workflow's YAML frontmatter supports. Keys shared with GitHub
Agentic Workflows keep their upstream meaning; loopkeep's own settings live
under x-loopkeep.*. Unknown keys are preserved with a warning.
on — triggers
on:
schedule:
- cron: "0 3 * * *" # aliases like daily/hourly/weekly also accepted
file-watch:
paths: ["inbox/**/*.pdf"]
events: [create, modify] # create | modify | delete
debounce_ms: 2000
min_size_kb: 1
coalesce: latest # latest | all
git:
events: [commit, branch-created]
branch: ["main", "release/*"]
author_not: ["loopkeep[bot]"]
run-completed:
workflow: "test-fixer"
status: [failed] # done | failed
if: event.stats.attempts >= 3
manual: {}
slack: # delivered via the Console
events: [app_mention] # app_mention | dm
channels: ["#ops"]
from_not: ["*bot*"]
issues: # GitHub events, delivered via the Console
repos: ["org/other-repo"] # omit to bind to this workspace's origin remote
Semantics — filters, coalescing defaults, chain depth — are covered in the triggers guide.
engine
engine:
id: claude # claude (Claude Code CLI, default) | api (Anthropic API, BYOK)
model: claude-sonnet-5
params: {} # engine-specific parameters
permission_mode: auto # the engine's own prompt behavior; loopkeep's gate is active in every mode
bypassPermissions is refused unless explicitly opted into; plan is not
used for runs.
tools
tools: [edit, bash, web-fetch]
The subset of agent tools the workflow may use. Unsupported tools warn.
permissions
Interpreted as input to the local safe-outputs gate, matching its gh-aw meaning.
safe-outputs
safe-outputs:
create-pull-request: {} # GitHub-native outputs work with your gh auth
local-commit: {}
local-file-write: {}
notify: {}
run-command: {} # always policy-evaluated
dispatch-workflow:
allowed: ["incident-triage", "inbox-processor"] # explicit allowlist, required
emit-output: {} # structured JSON for chaining; always auto-approved
Declaring a safe-output never bypasses policy
evaluation; emit-output alone is always auto-approved since it only feeds
downstream workflows.
mcp-servers
# In workspace or daemon config — the definition:
mcp-servers:
notion:
command: "npx -y @notionhq/notion-mcp-server"
env: { NOTION_TOKEN: "secret:notion-token" }
# In the workflow — a reference by name only:
mcp-servers: [notion]
Workflows reference servers by name; connection details live in configuration.
Inline connection info in a workflow is treated as a leaked credential — the
value is never echoed into logs or warnings. MCP tool calls go through policy
evaluation like any other action (tool: mcp:notion/create-page in policy
match terms).
x-loopkeep.concurrency
x-loopkeep.concurrency:
max: 1
on_limit: queue # queue (default) | skip | replace
The gh-aw-native concurrency: key is also honored with its upstream
meaning: cancel-in-progress: true behaves like replace, anything else
like queue.
x-loopkeep.budget
x-loopkeep.budget:
tokens: 200000
time_ms: 900000
steps: 30 # step N+1 fails the run with reason budget
daily_runs: 300
daily_tokens: 500000
Integers only. Time waiting on a human doesn't count against time_ms.
x-loopkeep.tags
x-loopkeep.tags: [deploy, migration]
Semantic labels for policy matching. Self-declared — hard safety rules should
match paths/tool instead.
x-loopkeep.execution_mode
x-loopkeep.execution_mode: attended # headless (default) | attended | auto
Where a run's agent lives:
headless(default) runs the agent as a subprocess; you supervise from the inbox.attendedruns it in a terminal multiplexer pane so you can watch and steer the same session directly. The policy gate still applies to every tool.autopicks by trigger: a manual run (you're there) becomesattended; an unattended firing — cron, a webhook — staysheadless.
attended needs a multiplexer loopkeep can drive; when none is available the run
falls back to headless. See the terminal multiplexer guide.
x-loopkeep.mux.space / x-loopkeep.mux.tab
x-loopkeep.mux.space: loopkeep # multiplexer session/workspace to place the pane in
x-loopkeep.mux.tab: automation # tab/window within it
Where an attended run's pane opens. Each falls back to multiplexer.options in
config, then to the built-in loopkeep / automation; the pane itself is named
after the run id. See the terminal multiplexer guide.
Secret references
{{ secret:<name> }} in the body, "secret:<name>" in config values. See
Secrets.
{{ trigger }}
Placement marker for the trigger context injected into every run. If absent, the context is prepended to the body.