Some situations require a human decision. The maintainer agent recognises these automatically and pauses until a maintainer responds, it never acts unilaterally on sensitive work.
Built-in escalation categories
These categories always escalate, regardless of your .maintainer.yml policy. They cannot be
disabled:
| Category | Trigger |
|---|---|
security |
Any issue or PR with a CVE, CWE, security label, or keywords like "vulnerability", "RCE", "SSRF", "injection". |
license |
A PR adds a dependency with an incompatible or unknown SPDX licence. |
cla |
A contributor raises a CLA or contribution-agreement question. |
The triggers are what the agent recognises while reading the thread, not a regex over your
issue text. The routing is not a judgment call: these three are hardcoded, and a
.maintainer.yml that tries to drop them is ignored.
Configurable always escalations
The category set is closed. escalate.always selects from a fixed vocabulary, it does
not define new triggers, and a name outside the set is a validation error on your
.maintainer.yml rather than a category that quietly never fires. Beyond the three above,
these are the values it accepts:
| Category | Trigger |
|---|---|
hostile_tone |
The issue, or the comment that woke the bot, is hostile or abusive. |
direct_mention |
Someone @-mentions the human maintainer rather than the bot. |
paid_customer |
Reserved for the reporter-identity signal; no loop emits it yet. |
hostile_tone and direct_mention are the default, so a repo with no escalate: block
still pages you for both. Writing the key REPLACES that default, it does not add to it:
escalate:
# Keep the defaults and page on nothing else. Dropping either one is opting out.
always: [hostile_tone, direct_mention]
ask_before_acting escalations
These pause the bot and ask the maintainer for explicit approval before continuing. They
email immediately too, the same as always. The difference is what the bot does next: an
always category stops the session outright, an ask_before_acting category leaves a
drafted action waiting for your yes or no.
Same closed vocabulary. The first four are the default set:
| Category | Default | Trigger |
|---|---|---|
breaking_change |
yes | The diff classifies as a major semver impact, or the PR title/body says BREAKING CHANGE. |
major_dep_bump |
yes | A package.json / Cargo.toml / pyproject.toml / lockfile dependency crosses a major boundary. |
first_time_contributor |
yes | The author has never had a PR merged in this repo. Never fires for a dependency bot. |
large_pr |
yes | Changed lines exceed pr.large_threshold_lines (default 500). |
release_publish |
no | Accepted by validation; no loop emits it yet. |
auto_merge |
no | Accepted by validation; no loop emits it yet. |
As with always, writing the key replaces the default set:
escalate:
# Ask about breaking changes and big diffs, and nothing else.
ask_before_acting: [breaking_change, large_pr]
How escalation works
- Detect, the agent matches the issue, PR, or event against the escalation rules.
- Comment, the bot posts a disclosure comment explaining exactly why it paused and what it needs from the maintainer.
- Email, an escalation email is sent immediately. Every category on this page routes email-immediate; only routine informational events fall through to the weekly digest.
- Wait, the agent parks the thread and does nothing until you decide.
- Resume, once approved, the agent continues the loop from the paused step.
- Audit, every escalation event (trigger, pause, approval, resume) is written to the audit log with actor and timestamp.
Bot-on-bot safety
The agent detects comments from CodeRabbit, Copilot, Dependabot, and Renovate and defers
to them rather than creating conflicting threads. Configure the list via pr.wait_for_other_bots.
The agent never responds to itself. A comment from developerz-ai[bot], the login
GitHub gives our App, does not trigger another loop iteration.
Resolving an escalation
There are no comment commands. A pending ask_before_acting action is resolved on the
control plane, never by typing something on the thread, so the decision carries an
authenticated actor rather than whoever could post:
- The escalation email. It carries signed approve and skip links; one click each.
- The dashboard. Approvals are listed under the PR, with approve and skip buttons.
- The API, for automation:
curl -X POST https://api.developerz.ai/v1/prs/$PR_ID/approve-pending \
-H "Authorization: Bearer dev_pat_…"
# or …/skip-pending to decline it
Scope: write:prs. Every decision is written to the audit log with the actor and a
timestamp. The bot may also suggest you apply a needs-maintainer-ack label, but that is a
human-readable marker for your own triage, it is not what resolves the escalation.