Skip to content

Policy & risk

Each repo’s .openloop/policy.yaml defines what agents may touch and how much autonomy they get. It is the contract between you and the loop, and it’s meant to be committed and code-reviewed like any other config.

scope:
allowGlobs: [] # if non-empty, changes must match at least one
denyGlobs: [] # hard blocks, always enforced
highRiskAreas: [] # touching these escalates the task's risk
riskClasses:
low-risk:
autoMergeAllowed: true
requiresHumanReview: false
medium-risk:
autoMergeAllowed: false
requiresHumanReview: true
high-risk:
autoMergeAllowed: false
requiresHumanReview: true
selfHealing:
enabled: true
allowedTaskKinds:
- lint-fix
- type-fix
- localized-test-fix
promotion:
lowRiskMode: auto-merge
mediumRiskMode: pull-request
highRiskMode: pull-request

Scope is checked in two places:

  1. Before the run: the scheduler checks the task’s planned scope against denyGlobs and highRiskAreas. Denied work is blocked; risky areas escalate the risk class (and therefore the promotion mode).
  2. After the run: with review enabled, the actual diff is compared against the globs. Drift outside allowed paths is a blocking finding and downgrades the promotion.

Globs are gitignore-style patterns:

scope:
allowGlobs:
- "src/**"
- "tests/**"
denyGlobs:
- "infra/**"
- ".github/workflows/**"
highRiskAreas:
- "src/auth/**"
- "migrations/**"

Tasks can additionally carry their own scope.paths (see tasks); the intersection applies.

Risk decides two things: whether a task needs approval before running (requiresHumanReview puts it in awaiting-approval) and how its output may be promoted (autoMergeAllowed). The defaults encode one opinion you can reshape:

  • low-risk: docs, comments, small fixes within allowed paths → may auto-merge with green validations.
  • medium-risk: the default for new tasks → pull request.
  • high-risk: auth, infra, migrations → pull request and explicit approval first.

Tasks whose scope doesn’t match any configured area get risk.defaultUnknownAreaClassification from project.json (default medium-risk), unknown means cautious.

Unattended self-repair is restricted to the kinds in selfHealing.allowedTaskKinds. The defaults (lint-fix, type-fix, localized-test-fix) let the daemon clear small breakages on its own; anything else (a broken build, failing integration tests) is deliberately blocked from running unattended and surfaces for human review instead.

With risk.requirePolicyForAutoMerge: true (default in project.json), auto-merge additionally requires a non-empty policy to exist. A repo without deliberate scope policy never merges autonomously.