cross-check.yaml
Configure CrossCheck for a repo by committing cross-check.yaml (or .yml) at the repo root. The file can declare additional checks and override a subset of repo settings normally edited from the dashboard.
When the file is present, the dashboard locks the fields it manages and shows a banner linking back to the file on GitHub.
Minimal example
version: 1
checks:
- name: new-tables-have-tests
description: Every new SQL model under models/ has a matching test under tests/.
That's it. The check runs on every PR alongside whatever check groups you've attached in the dashboard.
Full example
version: 1
config:
pass_behavior: approve_and_merge # auto-merge when all checks pass
general_review:
mode: enabled
required_for_approve: true
instructions: |
This is a Python service. Prefer asyncio idioms over threads.
enabled_tools:
- read_file
- list_dir
- glob
- grep
- read_diff
- bash
checks:
- name: no-print-in-prod
description: |
Production code under src/ must not contain print() statements.
Use the logger from src/observability/logger.py instead.
severity: high
tier_hint: deep
- name: migrations-have-down
description: Every Alembic migration in alembic/versions/ has a non-trivial downgrade() body.
severity: medium
Schema
Top level
| Field | Required | Type | Notes |
|---|---|---|---|
version | yes | integer (1) | Schema version. Only 1 is supported today. |
config | no | object | Sparse settings overrides. Omit fields to keep dashboard values. |
checks | no | list of check | Repo-local checks. Run alongside attached groups. |
config
Every field is optional. A field present in the file overrides the dashboard value; a field omitted falls back to the dashboard.
| Field | Type | Default |
|---|---|---|
pass_behavior | report_only | approve | approve_and_merge | report_only |
fail_behavior | report_only | configurable_block | always_block | report_only |
report_mode | comment_summary | comment_only | summary_only | comment_summary |
general_review.mode | off | enabled | off |
general_review.required_for_approve | boolean | false |
general_review.prompt_override | string (null/omit = built-in prompt) | null |
instructions | string | empty |
enabled_tools | list of built-in tool names (see tools) | dashboard default |
checks
Each check is one natural-language assertion the reviewer agent evaluates per PR.
| Field | Required | Type | Default |
|---|---|---|---|
name | yes | string (unique within file, ≤240 chars) | — |
description | yes | string (non-empty — write it like a code review note) | — |
severity | no | low | medium | high | medium |
tier_hint | no | auto | triage_only | deep | auto |
Enabled tools
If enabled_tools is set, it must be a subset of:
read_file, list_dir, glob, grep, read_diff, bash, write_file, apply_patch
Custom tools you've added in the dashboard cannot be enabled from the file (they may not exist when the file lands in a branch).
How statements are merged with dashboard groups
YAML checks run alongside dashboard-attached check groups. They never replace them. The PR comment lists them in a cross-check.yaml section, separate from your check groups.
This is intentional: if your org admin attaches a baseline "security" check group to every repo, a PR author can't drop those checks by adding a cross-check.yaml.
Where each part is read from
| Part | Source | Why |
|---|---|---|
checks: | PR head commit | Lets a PR introduce a check alongside the change it guards. |
config: | Default branch HEAD | Prevents self-approval — a PR can't flip pass_behavior: approve_and_merge on itself. |
Concretely: if you edit config.pass_behavior in a PR, the change does not take effect for that PR. It takes effect after the PR merges to your default branch.
When the dashboard re-reads the file
The dashboard's cached view of cross-check.yaml is refreshed:
- Automatically, on the next push to your default branch that touches the file.
- Manually, via the Refresh from GitHub button on the repo settings page.
- On checks-page load, if the cache is older than 5 minutes.
Errors
If cross-check.yaml is malformed:
- The PR review still runs, using your dashboard settings (the file is ignored for that run).
- A single comment is posted on the PR with the parse error. The same error won't re-post on subsequent pushes — fix it and the next push gets the file applied.
- A banner appears on the repo's Settings and Checks pages showing the error.
Common errors:
| Error | Fix |
|---|---|
unsupported version: N | Set version: 1. Future versions will be documented here. |
<field>: extra inputs are not permitted | You have a typo or unsupported key. Check the schema above. |
<field>: input should be 'auto', 'manual' (etc.) | Enum value isn't valid. Use one of the listed options. |
enabled_tools contains unknown tool(s): X | X isn't a built-in tool. See Enabled tools. |
duplicate check name: 'X' | Two checks[].name are the same. Rename one. |
cross-check.yaml is larger than the 64 KB cap | Split long description text or move it out of the file. |
Limits
- File size: 64 KB (after which the file is rejected with a parse error).
- The file must be at the repo root, named exactly
cross-check.yamlorcross-check.yml. Subdirectories are not searched. cross-check.yamlwins overcross-check.ymlif both exist.
YAML pitfalls
The file is parsed with YAML safe-load (no Python tag execution). A few things to watch:
- Use block scalars (
|) for multi-line descriptions. Plain folded strings collapse newlines into spaces. - Quote values that look like booleans.
name: onis interpreted asname: truein YAML 1.1 dialects — writename: "on". - Use 2-space indentation. Tabs are not valid YAML.