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.
  • attended runs it in a terminal multiplexer pane so you can watch and steer the same session directly. The policy gate still applies to every tool.
  • auto picks by trigger: a manual run (you're there) becomes attended; an unattended firing — cron, a webhook — stays headless.

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.