.maintainer.yml reference
Drop a .maintainer.yml at your repository root to configure the maintainer bot.
The file is YAML; all fields are optional and default-safe — an empty file with
only version: 1 is valid.
Schema autocomplete. Add
# yaml-language-server: $schema=https://developerz.ai/schemas/maintainer.v1.jsonas the first line of the file to get IDE validation and autocomplete.
Minimal example
# yaml-language-server: $schema=https://developerz.ai/schemas/maintainer.v1.json
version: 1
Full example
# yaml-language-server: $schema=https://developerz.ai/schemas/maintainer.v1.json
version: 1
tone:
voice: friendly
language: en
triage:
ask_for_repro: true
required_fields: [steps, expected, actual, version]
dedupe:
enabled: true
similarity_threshold: 0.85
qualify_into: [bug, feature, dupe, question]
handoff:
mode: webhook
webhook_ref: my-claude-code-hook
ask_reporter_first: true
pr:
wait_for_other_bots: [coderabbit, copilot]
required_checks: [ci]
large_threshold_lines: 500
auto_merge: true
human_merge:
branches: [main]
paths: [db/migrations/**]
release:
manager: release-please
channels: [github-release]
escalate:
always: [security, license, cla]
ask_before_acting: [breaking, major, first-time-contributor, large]
digest:
cadence: weekly
day: monday
time: "09:00"
timezone: maintainer
limits:
max_steps_per_session: 50
max_comments_per_issue_per_day: 10
kb:
repo_notes:
- note: "Auth lives in packages/auth; ignore the legacy auth-old/ tree."
by: "@maintainer"
pages:
- title: Architecture
purpose: How the worker dispatches jobs.
memory:
stale_after_days: 90
delete_unused_after_days: 40
review:
profile: chill
learnings: true
anti_noise:
severity_floor: minor
max_comments: 30
path_filters: ["!**/*.lock", "!dist/**"]
Root fields
| Field | Type | Required | Description |
|---|---|---|---|
version |
1 |
yes | Schema version. Must be 1. |
schema |
URL string | no | JSON schema URL for editor tooling. |
format_command |
string (shell command) | no | Formatter the bot runs before git add on the BYOVM (e.g. biome check --write ., gofmt -w .). Non-zero exit fails the commit rather than failing CI later. |
tone
Controls the bot's voice and language in all comments.
| Field | Type | Default | Description |
|---|---|---|---|
voice |
friendly | formal | terse |
friendly |
Persona used in all bot comments. |
greeting_template_ref |
string (ref name) | default |
Name of a greeting template configured in the dashboard. |
language |
BCP-47 string | en |
BCP-47 language tag for bot-generated copy (e.g. en, de, pt-BR). |
triage
Governs how new issues are triaged: reproduction requests, deduplication, and label outcomes.
| Field | Type | Default | Description |
|---|---|---|---|
ask_for_repro |
boolean | true |
Bot asks reporters for a reproduction before qualifying the issue. |
required_fields |
steps | expected | actual | version | environment | logs[] |
[steps, expected, actual, version] |
Checklist items the bot requests when ask_for_repro is enabled. |
dedupe.enabled |
boolean | true |
Vector-similarity deduplication against open issues. |
dedupe.similarity_threshold |
number 0–1 | 0.85 |
Cosine similarity cutoff (0–1). Issues scoring above this are flagged as duplicates. |
qualify_into |
bug | feature | dupe | question | docs | chore[] |
[bug, feature, dupe, question] |
Allowed outcome labels after triage. |
handoff
After qualification, the bot can hand off the issue to a coding agent.
mode = webhook fires a signed POST to the configured webhook; mode = label applies a
GitHub label your CI or coding agent can pick up; mode = none disables handoff.
When
mode = webhook,webhook_refis required. The webhook must be registered in the dashboard.
| Field | Type | Default | Description |
|---|---|---|---|
mode |
webhook | label | none |
none |
How qualified issues are handed off to a coding agent. |
label |
string | — | GitHub label applied when mode = label. |
webhook_ref |
string (ref name) | — | Dashboard webhook name used when mode = webhook. |
ask_reporter_first |
boolean | true |
Bot asks the reporter for consent before handing off. |
pr
Pull request review policy.
auto_mergeis on by default. The bot merges once the machine gates pass — CI green, review verdict, and the repo's branch protections — never blindly. To keep a human in the loop, setauto_merge: falseor list the sensitive branches/paths underhuman_merge. (Free-OSS-tier repos stay opt-in regardless.)
| Field | Type | Default | Description |
|---|---|---|---|
wait_for_other_bots |
coderabbit | copilot | dependabot | renovate[] |
[coderabbit, copilot, dependabot] |
Bots the maintainer bot waits for before acting on a PR. |
required_checks |
string[] | [] |
CI check names that must pass before the bot considers merging. |
large_threshold_lines |
integer > 0 | 500 |
PRs exceeding this line delta trigger an ask_before_acting escalation. |
auto_merge |
boolean | true |
On by default. The bot merges once the machine gates pass — CI green, review verdict, and branch protections satisfied. Set false (or use human_merge) to hand the merge back to a human. |
human_merge.branches |
string[] (globs) | [] |
Target-branch globs held for a human even when auto_merge is on (e.g. main, release/*). |
human_merge.paths |
string[] (globs) | [] |
Changed-path globs held for a human even when auto_merge is on (e.g. db/migrations/**, **/*.tf). |
release
Release automation. The bot can cut releases after a PR merges based on conventional commit history.
| Field | Type | Default | Description |
|---|---|---|---|
manager |
release-please | none |
release-please |
Release automation driver. |
channels |
github-release | npm | crates | pypi | rubygems[] |
[github-release] |
Registries the bot publishes to when cutting a release. |
escalate
Escalation rules. The bot always escalates security, license, and cla issues regardless
of configuration — these cannot be disabled.
| Field | Type | Default | Description |
|---|---|---|---|
always |
string[] | [] |
Categories that immediately email the maintainer (in addition to the hardcoded security, license, cla set). |
ask_before_acting |
string[] | [] |
Conditions that pause the bot and request explicit approval before continuing. |
Built-in always categories (non-configurable): security, license, cla.
Built-in ask_before_acting categories: breaking, major, first-time-contributor, large.
digest
Weekly (or daily/monthly) email digest sent to the maintainer.
| Field | Type | Default | Description |
|---|---|---|---|
cadence |
daily | weekly | monthly |
weekly |
How often the digest email is sent. |
day |
monday | tuesday | ... | sunday |
monday |
Day of the week for weekly digests. |
time |
HH:MM 24 h | 09:00 |
Delivery time in HH:MM 24 h format. |
timezone |
maintainer or IANA string |
maintainer |
maintainer uses the timezone from your GitHub profile. Otherwise any IANA zone (e.g. Europe/Berlin). |
limits
Hard caps that prevent runaway agent behaviour.
| Field | Type | Default | Description |
|---|---|---|---|
max_steps_per_session |
integer > 0 | 50 |
Hard cap on LLM tool-call steps per agent session. |
max_comments_per_issue_per_day |
integer > 0 | 10 |
Throttle on bot comments to prevent spam. |
coding
Knobs for the on-VM coding agent (BYOVM). Concurrency is a policy value, not a flag:
it caps how many isolated git worktree checkouts run in parallel for the repo.
| Field | Type | Default | Description |
|---|---|---|---|
max_concurrent_worktrees |
integer 1–64 | 1 |
Max parallel git worktree checkouts the on-VM coding agent runs for this repo (the WorktreePool semaphore capacity). Default 1 (single concurrency). |
ci
Fences for the self-hosted CI runners the fleet boxes serve. The per-workflow
opt-in is the label itself (runs-on: developerz-*); these two repo-wide
booleans sit on top of it. Both are defaulted, so a repo that commits a
policy but omits ci: still gets the safe fences.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Kill switch for the self-hosted CI lane. The developerz-* runner labels are the opt-in per workflow; set false to stop placing CI jobs on fleet boxes without editing every workflow file. |
public_repos |
boolean | false |
Non-negotiable public-repo fence. Off by default — a public repo lets any contributor open a PR whose workflow runs on your hardware, so the ci-assigner refuses to place a public-repo job unless a maintainer flips this on. |
mcp_servers
Array of external MCP servers the agent may call. Each entry:
| Field | Type | Default | Description |
|---|---|---|---|
name |
ref name string | — | Unique identifier for this server (used in audit logs). |
url |
URL | — | Base URL of the MCP endpoint. |
transport |
sse | http-streamable | stdio |
— | Transport protocol. |
token_ref |
string (ref name) | — | Dashboard secret name injected as Authorization: Bearer. |
allowed_tools |
string[] | ["*"] |
["*"] allows all tools. Otherwise an explicit allowlist. |
timeout_ms |
integer 1–60 000 | 5000 |
Per-request timeout in milliseconds (max 60 000). |
Ref names (token_ref) resolve to secrets registered in the dashboard.
The value is never stored in the policy file.
Example
mcp_servers:
- name: linear
url: https://mcp.linear.app/sse
transport: sse
token_ref: linear-api-key
allowed_tools: [create_issue, search_issues]
timeout_ms: 10000
kb
Repo-steered knowledge — a committed, reviewable way to correct or seed the agent's understanding of your repo. The bot reads it during triage alongside what it has learned on its own, so a short note here grounds answers without any code change.
kb.repo_notes
Short, attributed facts. Combined note length is capped at 10 000 characters.
| Field | Type | Default | Description |
|---|---|---|---|
note |
string 1–1 000 | — | The fact itself (1–1 000 chars). |
by |
string 1–120 | — | Who asserted it — a GitHub login, team, or name. Optional. |
kb.pages
An explicit table-of-contents that overrides the agent's auto-clustering.
Up to 30 pages; titles must be unique and any parent must name another page.
| Field | Type | Default | Description |
|---|---|---|---|
title |
string 1–120 (unique) | — | Page title (1–120 chars, unique across pages). |
purpose |
string 1–500 | — | One-line description of what the page covers (1–500 chars). |
parent |
string (existing title) | — | Title of the parent page for a shallow tree. Must name an existing page. |
notes |
string ≤2 000 | — | Optional longer prose for the page (≤2 000 chars). |
kb.memory
Retention and consolidation settings for the bot's accumulated memories (learnings from
past reviews, issues, and discussions). Distinct from repo_notes and pages (which
are explicit, human-authored steering). Memory entries that grow stale or unused are
pruned or merged automatically by the nightly gardener; these knobs tune that cleanup.
| Field | Type | Default | Description |
|---|---|---|---|
stale_after_days |
integer ≥7 | 90 |
A memory entry untouched this long (days) is eligible for consolidation into a KB page. |
delete_unused_after_days |
integer ≥7 | false |
40 |
consolidate |
boolean | true |
Merge/summarize stale-but-still-referenced memory entries instead of leaving them to rot. |
Consolidation. When consolidate: true, the gardener summarizes stale-but-still-
referenced memories into KB pages via the account's BYOK model. When false, they are
left as-is (or deleted if unused). Deletion. Set delete_unused_after_days to
false to keep all memories forever; set a numeric value ≥7 to delete memories that
have never been recalled (or recalled before the deletion cutoff).
Example
kb:
repo_notes:
- note: "Auth lives in packages/auth; ignore the legacy auth-old/ tree."
by: "@maintainer"
- note: "We never accept PRs that touch the vendored/ directory."
pages:
- title: Architecture
purpose: How the worker dispatches jobs.
- title: Dispatch
purpose: Two-phase pull queue + per-entity lock.
parent: Architecture
memory:
stale_after_days: 90
delete_unused_after_days: 40
consolidate: true
review
Native AI code review — a hosted, CodeRabbit-compatible reviewer running on
your BYOK model, included in the seat. The native review: block is a
superset of .coderabbit.yaml (adds anti-noise floor/cap and KB learning
write-back); when the bot reads a .coderabbit.yaml it honors most fields but
defers reviews.tools and reviews.finishing_touches (surfaced as warnings).
Precedence. If a repo has both a
.coderabbit.yamland areview:block, thereview:block wins (.maintainer.yml>.coderabbit.yaml). The block is optional — omit it and the bot reads your existing.coderabbit.yaml(if any) or falls back to the built-in review defaults. Migrating off CodeRabbit is a no-op: keep the file, uninstall their app.
Bot etiquette. The reviewer always defers when another review bot (e.g. CodeRabbit, Copilot, Dependabot) is active on the repo — no bot-on-bot loops, no override. To hand review to the native reviewer, uninstall the other bot.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Master switch for the native reviewer. |
force |
boolean | false |
Review even when another review bot (CodeRabbit) is active on the repo. Off by default so two bots never double-review the same PR; turn on while migrating off CodeRabbit. |
profile |
chill | assertive |
chill |
chill posts fewer, higher-signal comments (raises the default severity floor); assertive surfaces more, including minor issues. |
language |
BCP-47 string | en-US |
BCP-47 language tag for review copy (e.g. en-US, de). |
tone_instructions |
string ≤4 000 | "" |
Free-text steering for the reviewer’s voice and emphasis. |
request_changes |
boolean | false |
Emit a request_changes verdict (not just a comment) when a blocking finding exists. |
high_level_summary |
boolean | true |
Post a single top-level walkthrough/summary comment on the PR. |
learnings |
boolean | true |
Write review learnings back to the repo knowledge base (kb) so later reviews cite them — the CodeRabbit-style moat feature. |
chat_auto_reply |
boolean | true |
Reply to review comment threads automatically. |
labels.suggest |
boolean | false |
Suggest labels for the PR. |
labels.auto_apply |
boolean | false |
Apply suggested labels automatically. |
reviewers.suggest |
boolean | false |
Suggest human reviewers for the PR. |
reviewers.auto_assign |
boolean | false |
Assign suggested reviewers automatically. |
auto_review.enabled |
boolean | true |
Review PRs automatically on open/synchronize. |
auto_review.incremental |
boolean | true |
On a re-push, review only the new commits rather than the whole diff again. |
auto_review.drafts |
boolean | false |
Review draft PRs too. |
auto_review.base_branches |
string[] | [main] |
Only auto-review PRs targeting one of these base branches. |
auto_review.ignore_title_keywords |
string[] | [] |
Skip PRs whose title contains any of these (e.g. WIP, DO NOT MERGE). |
anti_noise.severity_floor |
critical | major | minor | nit |
minor |
Drop findings below this severity. Blocking findings always bypass it. Overrides the profile-derived floor. |
anti_noise.max_comments |
integer 1–200 | 30 |
Cap on non-blocking inline comments per review. Blocking findings are always kept. |
anti_noise.dedupe |
boolean | true |
Collapse repeated findings against prior reviews on re-push. |
path_filters |
string[] (globs) | [] |
Glob filters applied before chunking; a leading ! excludes a path. |
path_instructions |
{ path, instructions }[] |
[] |
Per-glob free-text guidance the reviewer applies to matching files. |
Anti-noise. Quiet by default (their #1 complaint): findings below
anti_noise.severity_floor are dropped, volume is capped at max_comments, and
repeats are deduped on re-push. Blocking findings (principle/gate violations)
always bypass both the floor and the cap.
Example
review:
profile: chill
request_changes: true
learnings: true # write review learnings back to kb
anti_noise:
severity_floor: minor
max_comments: 30
auto_review:
base_branches: [main, release/*]
ignore_title_keywords: [WIP, "DO NOT MERGE"]
path_filters: ["!**/*.lock", "!dist/**"]
path_instructions:
- path: "packages/**/*.ts"
instructions: "Enforce the repo principles in docs/idea/principles.md."
This page is generated from packages/policy/src/schema.ts. Run bun run scripts/policy-doc.ts to regenerate.