Skip to main content
Check Run Agents are customizable AI agents that run on every pull request open, push, and manual rerun. They can access your codebase, git history, and connected integrations, and you define what to check, how to format output, and what actions to take. Macroscope automatically runs two built-in check runs on every PR: Correctness (catches runtime bugs and logic errors) and Approvability (evaluates merge readiness). Your custom agents appear alongside them in the Checks tab.

Getting Started

  1. Create a .md file in .macroscope/check-run-agents/ (e.g. .macroscope/check-run-agents/web-review.md)
  2. Configure the frontmatter fields you need (all optional) and write your instructions:
  1. Commit and push to your default branch.
Agent files work like any other code change. Macroscope reads them from the most recent commit on the pull request, so pushing a new or edited agent to your own PR takes effect on that PR right away. Other open PRs don’t pick it up until the change is merged and their branches include it.

File Layout

Each .md file in .macroscope/check-run-agents/ becomes its own agent, scoped to its repo. Start with one file and split only when you need different configurations (e.g. different tools or input modes). A typical repo structure:
Subdirectories are walked recursively, so you can group agents by team or service in a monorepo:
Nesting is purely organizational — an agent behaves the same wherever it lives. Only *.md files are read (README.md and others are ignored), and each agent’s title must be unique across subdirectories; duplicates are auto-suffixed with a number.
The filenames approvability.md and ignore.md are reserved — they cannot be used as check run agent definitions. approvability.md configures custom approvability rules, and ignore.md controls which files are excluded from code review and Check Run Agents. These files always live in the .macroscope/ root, not in the check-run-agents/ subfolder.
Existing setup? If your check run agent files are in .macroscope/ instead of .macroscope/check-run-agents/, they will continue to work. We recommend moving them to the subfolder when convenient — the root location will stop being read in a future release. See Migrating for a one-line move.
If you already use a CLAUDE.md to guide how AI writes code, embed it directly in your instructions with @/CLAUDE.md to keep coding and review standards in sync. See Importing Files.

File Format

Each file has two parts: an optional YAML frontmatter block for settings and a markdown body with your instructions. Every field is optional — omit frontmatter entirely and defaults apply, with the filename as the title.

What gets reviewed

include and exclude: scoping by file Use include to review only matching files, exclude to skip files, or both — include narrows the universe first, then exclude carves out exceptions. For example, include: ["src/**"] + exclude: ["src/gen/**"] reviews all src/ files except generated ones. Both accept the same glob syntax ("*.go", "src/**", "services/auth/**/*.go"). Like .gitignore, a pattern without a / matches at any depth, so "*.go" also matches src/main.go.
.macroscope/ignore.md: repository-wide exclusions To exclude files from every check run — not just one agent — add patterns to your repository’s .macroscope/ignore.md file. A file is out of scope when it fails the agent’s include/exclude pipeline or matches a repository-wide ignore pattern. An agent’s include overrides .macroscope/ignore.md. A file matching the agent’s include (and not carved out by its exclude) is reviewed even if an ignore pattern also matches — the output warns which ignore pattern was overridden. This lets a targeted agent review files the rest of your checks ignore (e.g. generated code). Agents without include always respect .macroscope/ignore.md. When every changed file is out of scope, no agent runs and nothing is billed. What appears in the Checks tab depends on which patterns did the filtering: When some files are in scope, the agent runs and both pattern sets bound what it reports:
  • In full_diff mode, the agent sees the entire PR diff for context, and its prompt lists the include/exclude/ignore patterns to focus on — noting, for agents with include, that files matching the include patterns are in scope even where ignore patterns match.
  • In code_object mode, out-of-scope code objects are filtered out before the agent runs — the agent never sees them.
  • In pr_metadata mode, scoping works exactly like full_diff — the changed files decide whether the agent runs at all — but the diff itself is not in the prompt.
  • In all modes, review comments the agent attempts to post on out-of-scope files are dropped.
Use include/exclude for per-agent scoping (e.g. “only Go files”) and .macroscope/ignore.md for repo-wide exclusions that apply to all checks (e.g. generated code, vendored dependencies).
input: full diff, code object, or PR metadata In pr_metadata mode, everything except the prompt input works exactly like full_diff: include/exclude patterns still decide whether the agent runs based on the changed files, requiredStatusCheck skips the same way, and the default tools are unchanged — so an agent with modify_pr can fix the metadata it flags, and one with browse_code or git_tools can still consult the changes when its instructions require it. Only the diff is kept out of the prompt, which is what makes this mode the cheapest.

Reporting and blocking

conclusion: blocking vs non-blocking By default, the conclusion is capped at neutral: issues show up in the Checks tab but never block merging. Set conclusion: failure to let the agent block PRs when it finds issues. Even with failure, the check still reports success when nothing is wrong. The setting only raises the ceiling on severity, not the floor.
requiredStatusCheck: branch protection required checks If you add a Check Run Agent to a branch protection rule’s required status checks, GitHub treats a check that never reports as failing — the PR shows “Expected — waiting for status to be reported” indefinitely and cannot merge. By default, when include/exclude filters out every changed file, the check run is not created (see Repository-Wide Exclusions). That keeps narrowly-scoped agents out of the Checks tab in monorepos, but blocks PRs when the check is required. Set requiredStatusCheck: true to make the agent always report a conclusion:
When no files match, the check run is created and concluded as skipped, which GitHub counts as passing. No agent runs and nothing is billed — only how the skip is reported changes. When files match, behavior is identical to any other agent, for all three input modes. It also takes priority over the agent’s labels, authors, and targets filters and the repository’s Skip PRs by Author / Label / Target settings: on excluded PRs the check is still created and concluded as skipped, with a message naming the filter (Macroscope shows a configuration note when you combine them). Filters are evaluated when the check runs, so adding a matching label after opening a PR doesn’t re-run a skipped check — it re-evaluates on the next push or @macroscope mention.
requiredStatusCheck guarantees a conclusion only when the agent is scheduled at all. The check run is never created — and required-check PRs will block with no Macroscope-side remedy — if:
  • Check Run Agents is disabled for the repo (repo setting, account override, or the feature being unavailable), or
  • the agent’s .md file is deleted or renamed while the branch protection rule still requires the check.
If either of those applies, update the branch protection rule to stop requiring the check.
Without include/exclude patterns or applicability filters, requiredStatusCheck: true is a harmless no-op — the agent always runs and reports a conclusion anyway — unless your repository’s Skip PRs by Author / Label / Target settings can exclude PRs, in which case excluded PRs get a check run concluded as skipped instead of no check run.

When an agent runs

Applicability filters labels, authors, and targets decide whether an agent runs for a PR, based on PR metadata — peers of include/exclude, which decide which files it reviews. Each is an inclusion filter: when set, the PR must match at least one entry; when omitted, it adds no constraint. Axes combine with AND — every configured axis must match.
authors matches the PR author’s GitHub login as a member of your workspace. PRs opened by bots (e.g. dependabot[bot]) or by accounts outside your workspace don’t resolve to a workspace login, so an agent with an authors filter never runs on those PRs.
targets is a list of branch names, and supports a special sentinel value:
  • Omitted → runs regardless of target branch (default).
  • only_default_branch → runs only when the PR targets the repository’s default branch. This is the common case for agents that should only gate merges into main.
  • One or more branch names → runs only when the PR targets one of them, e.g. targets: [main, release/v1].
Always enforced. Like include/exclude, these filters apply on every trigger, including manual @macroscope mentions. (Repository-level Skip PRs by Author / Label / Target settings, by contrast, are bypassed on manual mentions.) A filtered-out agent is not created — it never appears in the PR’s Checks tab — unless it sets requiredStatusCheck: true, in which case the check run is created and concluded as skipped so branch protection never blocks on it. Overriding repository skip settings. A configured axis overrides the matching repository-level Skip PRs by Author / Label / Target setting. If your repo globally skips PRs by alice, an agent with authors: [alice] still runs for her. An omitted axis leaves the repo setting in force.
Blank entries are ignored and duplicates are removed automatically.
waitsFor: prerequisite steps Use waitsFor to delay an agent until other CI steps finish. It shows as “in progress” on GitHub while waiting, then runs once all prerequisites complete — useful for reading another check’s results (e.g. summarizing Correctness) or running last after all CI. Named prerequisites List the exact check run names your agent depends on, as they appear on the PR’s Checks tab:
See the name-matching rules below for how GitHub derives each of these names. Wildcard: run after everything else Use "*" to wait for all other check runs on the commit to complete before running:
Custom timeout The default wait is 20 minutes. Set waitsForTimeout to change it (1–60 minutes):
When a prerequisite is published late There are two separate clocks. waitsForTimeout is how long a prerequisite may take to finish. A second, shorter clock — the discovery window, 60 seconds by default — is how long it may take to appear on the commit at all. If a named prerequisite has not shown up on the Checks tab by the time the discovery window closes, the agent skips with “not found” rather than waiting out the full timeout for a check that a typo means will never exist. Sixty seconds covers ordinary webhook lag, but not a check that is published conditionally. Two common cases:
  • The job is gated behind an earlier job (e.g. a detect-changes job that decides whether to run it), so GitHub does not create its check run until that job finishes.
  • Your workflow uses concurrency with cancel-in-progress: true, and a new push arrives while the previous run is still cancelling. The new run exists but stays pending, and none of its check runs are published until it starts — which can take several minutes.
Set waitsForDiscoveryTimeout (minutes, 1–60) to give such a check longer to appear:
The trade-off is real in both directions. A long discovery window tolerates a check that is published late; it also means a check that will never appear — a typo, or a job this PR does not trigger — takes that long to skip instead of failing fast in a minute. Set it to roughly the longest delay you actually observe, not to the maximum.
The discovery window is always capped at waitsForTimeout: it is a slice of the overall wait, never an addition to it. Both fields govern requires exactly as they govern waitsFor.
With waitsFor: ["*"] (“go last”), the discovery window is a minimum wait, not a deadline. A wildcard agent has no list of names to look for, so it must outlast the window before it can conclude that every check on the commit has finished — raising waitsForDiscoveryTimeout to 10 delays every run of that agent by 10 minutes. With named prerequisites there is no such cost: as soon as every named check has appeared, the wait moves on.
Behavior Name matching Names are matched exactly, case-insensitive against the check run name as it appears on GitHub.
The easiest way to find the correct name is to open the PR’s Checks tab on GitHub and copy the check run name exactly as it appears there. That string is what you put in waitsFor — no guessing required.
Macroscope checks use the prefix Macroscope - , e.g. "Macroscope - Correctness Check". GitHub Actions jobs appear as individual check runs, and GitHub derives the check run name in one of two ways depending on whether the job declares an explicit name: field in the workflow YAML:
  1. Job has an explicit name: field → the check run name is exactly that name: value (the workflow name is not prepended).
    Here you would use waitsFor: ["lint"].
  2. Job has no name: field → GitHub falls back to the composite format workflow_name / job_id.
    Here you would use waitsFor: ["ci / lint"].
In both examples the job id (the YAML key) is lint; the only difference is whether a name: field is present. When in doubt, trust the Checks tab — it shows the exact string GitHub computed.
waitsFor matches individual jobs (check runs), not entire workflows (check suites). To wait for all jobs in a multi-job workflow, list each job’s check run name, or use waitsFor: ["*"] to wait for everything.
Wildcard mode details When waitsFor: ["*"] is set, the agent waits for every check run on the commit except:
  • Itself — the agent never waits for its own check run.
  • Other wildcard agents — if multiple agents use waitsFor: ["*"], they exclude each other and run in parallel once all non-wildcard checks finish.
This means you can have multiple “go last” agents without deadlock. Prerequisite conclusions The agent receives a Markdown table of prerequisite outcomes in its prompt context, so your instructions can branch on pass/fail:
Any conclusion (success, failure, neutral, etc.) satisfies the dependency — waitsFor controls ordering, not gating on success. Use requires when you want the agent skipped unless its prerequisites passed.
requires: only run if the prerequisites passed waitsFor delays an agent; it never cancels one. If your lint job fails, a waitsFor: ["lint"] agent still runs — and still costs money. requires is waitsFor plus a gate on the outcome. The agent waits exactly the same way, and then runs only if every prerequisite passed. Otherwise it is skipped before the agent starts, so a run against a red build costs you nothing:
Everything waitsFor supports, requires supports identically — the same name matching, the same waitsForTimeout (there is no separate timeout field), and the same "*" wildcard. The 10-entry limit counts distinct check names across both fields together:
Use both together waitsFor and requires are independent. A check named in either is waited for; a check named in requires must also pass. So you can wait for one thing and gate on another:
That is the common shape: you want a step’s result in the agent’s context regardless of how it ended, but you don’t want to pay for the agent at all if something cheap and fundamental is broken. Prerequisite outcomes for everything waited on are passed to the agent (see Prerequisite conclusions), so waiting without gating is useful rather than merely permissive. Naming the same check in both fields is fine and means exactly what it reads as — wait for it, and require it to pass. It is not an error and produces no warning. You can also combine the wildcard with a named gate — “run last, but only if lint passed”:
What counts as passing requires uses GitHub’s own definition, so it agrees with what your branch protection rules already do: GitHub’s branch protection documentation puts it directly: “Required status checks must have a successful, skipped, or neutral status before collaborators can make changes to a protected branch.”
A skipped check passes. A conditional CI job that did not run for this PR — a paths: filter that did not match, an if: that evaluated false — reports skipped, and GitHub lets that merge. Treating it as a failure would block your agent on jobs that were never meant to run.
When a prerequisite fails The agent is skipped with a “Prerequisite check(s) did not pass” message naming each blocking check and how it ended:
A required check that never appeared on the commit blocks too, and is listed as not found on this commit. The agent only runs when every required check is known to have passed. To run the agent regardless of a particular step’s outcome — for example so it can read and summarize a failure — put that step in waitsFor instead.
requires is a check run agent field. .macroscope/approvability.md accepts waitsFor only; requires there is ignored with a warning, because skipping the Approvability check when CI is red would remove the signal you rely on to decide whether a PR needs human review.
Circular dependencies If agents form a dependency cycle (A waits for B, B waits for A), Macroscope detects it at parse time — waitsFor and requires form the same dependency graph, so a cycle built from either is caught the same way. Every agent in the cycle skips immediately with an error naming the cycle, so you get fast feedback instead of a silent timeout.
maxRuns: run limits By default a Check Run Agent runs on every push to a pull request. Set maxRuns (a positive integer, e.g. maxRuns: 3) to cap how many times it runs on a single PR. The limit is per pull request: each PR has its own independent count. Once the cap is reached, later pushes still create the check run but immediately conclude it skipped with a “Maximum runs reached” message, so it stays visible in the Checks tab. To run the agent again past the cap, comment @macroscope review on the pull request. (Re-running from the Checks tab does not bypass the cap.) Omitting maxRuns (the default), or setting it to 0 or less, means no limit. To turn a check off, remove its .macroscope/*.md file; maxRuns: 0 does not disable it.
maxBudgetPerRun: spend limits Set maxBudgetPerRun to limit runaway or degenerate behavior in a single run by your agent:
Fractional amounts work, so you can set the small caps those settings also allow:
The agent checks its accumulated cost after each turn and stops as soon as it reaches or exceeds the cap. The check run then concludes neutral with a “Budget reached” message, and the conclusion page reports what the run actually spent. Because the agent stops mid-investigation, its findings may be incomplete — treat a budget-reached conclusion as “this run did not finish”, not “this run found nothing”. If an agent hits its cap regularly, either raise maxBudgetPerRun or narrow the check by adjusting your prompt so each run does less work. Work already done when the cap is reached is still billed. The cap represents a boundary against future turns running; it does not make the work leading up to it free. The limit is per run, not per pull request. An agent with maxBudgetPerRun: 5 that runs three times on a PR may encounter the cap all three times. Omitting maxBudgetPerRun (the default), or setting it to 0 or less, means no limit. The smallest cap that can be enforced is 0.00001 (0.00001);anythingsmallerbutstillpositiveisraisedtoit.Valuesabove100000(0.00001); anything smaller but still positive is raised to it. Values above `100000` (100,000) are clamped down to that maximum. Either adjustment is noted as a warning on the check run’s conclusion page.
maxBudgetPerRun is not supported with input: code_object. That mode runs one agent per changed code object, so a per-run cap would let a single check spend several times the amount you set. A check configured that way fails with a configuration error instead of running — switch to input: full_diff or input: pr_metadata, or remove maxBudgetPerRun.

Tools

Included by default
Specifying tools: in frontmatter overrides the defaults. To keep defaults and add more, list them all. Each check run’s conclusion page shows an ordered log of the agent’s tool calls; set showToolCalls: false in frontmatter to hide it.
Additional tools
Connected tools require the integration in Settings > Connections. Missing connections are silently disabled.

Models & Pricing

Choose which model powers your agent, tune reasoning and effort, and see per-model pricing and cost controls.

Output

Results appear in three places:
  1. Check run details. Click into the check in the Checks tab to see the full report: title, summary, and detailed findings. Your instructions influence how this output is structured and formatted.
  2. Inline PR comments. The agent posts comments directly on specific lines in the PR diff, attributed to the check name.
  3. Issue comments. The agent can also post top-level comments on the PR itself for broader findings or summaries.
Inline PR comment from a Check Run Agent Check Run Agents in the GitHub Checks tab

Instructions

Controlling output format You can control formatting in your instructions. Some examples:
  • “Use a markdown table with columns: file, line, issue, severity”
  • “Group findings by priority, critical first”
  • “Use 🔴 🟡 🟢 emoji for severity levels”
  • “Start with a one-line summary, then list details”
  • “If no issues found, just say ‘All clear’ with no extra detail”
  • “Format as a checklist so the reviewer can tick items off”
  • “Be concise. Each bullet point should be under 20 words”
Writing good instructions
  • Be specific. “Review for quality” is too vague. “Flag any function over 50 lines without a doc comment” is actionable.
  • Define severity. Spell out what critical vs minor means for your team.
  • Don’t replicate the Correctness check run. It already catches runtime bugs. Focus on your team’s conventions and workflows.
  • Scope with include, exclude, or both. If your check only applies to Go files, use include: ["*.go"]. If it applies to everything except lock files, use exclude: ["*.lock"]. Use both together to narrow to a set of files while carving out exceptions (e.g. include: ["src/**"] + exclude: ["src/gen/**"]).
  • Give the agent permission to do nothing. “If nothing applies, report that no issues were found” prevents invented findings.
  • Use sections in your instructions. Markdown headings (##) in the body help the agent organize its work and output.
  • Reference specific paths. “Check files in services/auth/” is better than “check auth code.”
  • Tell it what not to flag. “Ignore test files” or “don’t flag TODOs in draft PRs” reduces noise.
  • Don’t paste your standards in. If a standard already lives in a file in your repo, embed that file with @path/to/file.md instead of copying it. See Importing Files.

Importing Files

Your instructions can embed another file from your repository by writing @path/to/file.md. The file’s contents are spliced in exactly where the directive appears, so the agent reads them as part of its instructions. This is the same syntax Claude Code uses for CLAUDE.md imports, so standards you already reference that way work here unchanged.
Use it to keep one source of truth. A standard that lives in docs/, a CLAUDE.md, or a team playbook can be embedded by several agents at once, and editing that file updates every agent that imports it — no copy-paste to keep in sync.
Imports are expanded in the body only, not in frontmatter. The imported file’s own frontmatter (if it has any) is stripped, so only its content reaches the agent.

How paths resolve

A path resolves relative to the directory of the file the directive is written in — the same rule Claude Code uses. Starting the path with / resolves it from your repository root instead, which is usually what you want from an agent file nested in .macroscope/check-run-agents/. From an agent at .macroscope/check-run-agents/go-review.md: Any file type works — .md, .txt, a config file, a code sample. The contents are inserted as text.

What is not an import

A directive is only recognized when the path is path-structured: it contains a / or a file extension. That keeps ordinary prose and code from being mistaken for imports:
  • Bare words are left alone. @Override, @Test, @param, @dataclass, @property, and handles like @macroscope are not imports.
  • An @ inside a word is left alone. support@yourcompany.com stays an email address.
  • Code is never expanded. A directive inside backticks (`@docs/style.md`) or a fenced code block is left as written, so you can quote the syntax or include a diff without triggering an import.

Nesting

An imported file can import others, up to 4 hops from the agent file. Each file’s directives resolve relative to its own directory, so docs/standards/all.md can pull in its neighbours with @go.md and stay correct no matter which agent imports it:
Import cycles are detected — if a file imports something that imports it back, the cycle is reported rather than expanded.

Limits

The size cap is on the sum of everything embedded, so a handful of directives cannot quietly assemble a prompt far larger than any single file in your repo.

When an import can’t be expanded

The agent still runs. An import that can’t be expanded is left in the prompt as literal text, and the reason appears as a warning on the check run’s conclusion page — a broken path never costs you the review. Repeated problems are reported once, not per occurrence. That covers a path that doesn’t exist, a cycle, nesting past 4 hops, and the size and count limits above. If a check’s output looks like it’s missing a standard you expected, the conclusion page is the place to look.

Which commit files come from

Imported files are read from the same commit as the agent file itself: the most recent commit on the pull request. Editing an imported standard takes effect on the PR that edits it, and on other PRs once the change is merged and their branches include it — exactly like editing the agent file.
Paths must stay inside the repository, and paths under .git cannot be imported. Symbolic links are not followed — a path is resolved literally.

Example

Consolidate related rules into one check rather than many files. This web team example handles several concerns in one agent, saved as .macroscope/check-run-agents/web-review.md:

Migrating

From the .macroscope/ root to the subfolder. If you have check run agent files in the root .macroscope/ directory, move them into the check-run-agents/ subfolder:
Nothing else changes — the format and frontmatter are identical. Commit and merge to your default branch, and new PRs use the new location automatically. approvability.md and ignore aren’t check run agent files — leave them in the .macroscope/ root. From Custom Rules. Check Run Agents replace Custom Rules. Move an existing macroscope.md file’s rules into .macroscope/check-run-agents/my-rules.md; they become the instructions body, and you can optionally add frontmatter for model, tools, and input mode. They go further, too: agents can browse the codebase, query git history, post to Slack, check Sentry, and more, with structured output in the Checks tab instead of just inline comments.
Looking for model options, pricing, or cost controls? See Models & Pricing.