---
title: "Keep advisory CI checks non-blocking without losing signal"
description: "A slow, non-required check on the PR critical path is pure wait. How to narrow or async advisory checks in GitHub Actions, without disabling real tests."
url: https://starsling.dev/best-practices/github-actions/keep-advisory-checks-non-blocking
canonicalUrl: https://starsling.dev/best-practices/github-actions/keep-advisory-checks-non-blocking
---

# Keep advisory checks off the critical path

Best practice: Required-check Hygiene. Last updated: 2026-07-16

When a slow GitHub Actions check sits on the PR critical path but isn't required to merge and only produces advisory output (a size comment, a preview), narrow its trigger or make it async, but never de-scope a check that actually runs tests.

## Table of contents

- [Do this](#do-this)
- [Avoid this](#avoid-this)
- [Why it matters](#why-it-matters)
- [When to use](#when-to-use)
- [Verify on your repo](#verify)
- [Sources](#sources)

<a id="do-this"></a>

## Do this

Genuinely advisory checks (a bundle-size comment, a preview deploy, a non-blocking lint annotation) don't rerun on every push and don't gate the merge. They run once per PR, or post asynchronously, so they inform without adding to the wait. Real tests and build validation stay on every push, for those, the lever is to make them faster, not to stop running them.

_Advisory check runs once per PR, not every push_

```yaml
# A size-diff comment is advisory: run it on open/ready, not on every push.
#
# Keep "reopened". The DEFAULT types are [opened, synchronize, reopened], and naming
# any types replaces that list wholesale: drop reopened and a PR that is closed and
# reopened never runs the check again. What you are removing here is "synchronize",
# the every-push trigger. That is the whole point.
on:
  pull_request:
    types: [opened, ready_for_review, reopened]   # note: no "synchronize"
```

<a id="avoid-this"></a>

## Avoid this

The advisory check reruns on every push and sits on the critical path with no gating value.

_Advisory check reruns on every push and blocks_

```yaml
on:
  pull_request:   # the size-diff comment reruns on every push and sits on
                  #   the critical path, even though no one merges on it
```

<a id="why-it-matters"></a>

## Why it matters

The slowest check holding up a PR is sometimes one that isn't even required to merge. If it's purely advisory, running it on every push is friction with no gating value. The critical distinction: a non-required check that runs real tests or validates a build is still load-bearing developer signal, de-scoping that hides failures. Only narrow checks whose output is genuinely advisory, and if you can't confirm a check is non-required (branch-protection data is unreadable), leave it alone.

<a id="when-to-use"></a>

## When to use

**Use it when:** A check that is high on the critical path, absent from the required-status list, and whose output is genuinely advisory (comment, preview, non-blocking annotation) with no downstream job depending on it.

**Be careful when:** Never de-scope a check that runs tests or validates a build, one that feeds a required aggregator, or one whose required status you can't confirm. For a real gate, speed it up (cache, shard, scope) instead of dropping it.

<a id="verify"></a>

## Verify on your repo

Hand this prompt to your coding agent to audit and fix this practice in your own repo:

Inspect this repo's .github/workflows for a check that sits high on the PR critical path but is purely advisory (a bundle-size comment, a preview deploy, a non-blocking annotation) and is not required to merge. For a genuinely advisory check, narrow its trigger so it does not rerun on every push (for example `types: [opened, ready_for_review, reopened]`, which drops only `synchronize`). Keep `reopened`: naming any `types` replaces the default list wholesale, so omitting it means the check never runs again on a reopened PR, or make it post asynchronously. Do NOT de-scope any check that runs tests, validates a build, feeds a required aggregator, or whose required status you cannot confirm: for a real gate the fix is to make it faster, not to stop running it. Show me the diff and open a PR rather than applying it blindly.

Ground these changes in the upstream docs before you edit: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows, https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks. If you cannot fetch them, say so rather than guessing, and cite what you used in the PR description.

Prefer to check by hand:

- Find the checks highest on your PR critical path (longest median duration in the check-runs list).
- Cross-reference the required-status list (branch protection / rulesets). If the slow check isn't required AND is genuinely advisory, it's a candidate.
- Enumerate consumers first: does anything `needs:` it, read its output, or gate a deploy/comment on it? If so, it's required-in-effect, leave it.

<a id="more-best-practices"></a>

## More best practices for GitHub Actions

- [Cut CI queue time in GitHub Actions](https://starsling.dev/best-practices/github-actions/cut-queue-time)
- [Filter workflows by path in GitHub Actions](https://starsling.dev/best-practices/github-actions/path-filter-workflows)
- [All CI best practices](https://starsling.dev/best-practices/github-actions)

<a id="faq"></a>

## FAQ

### How do I tell an advisory check from a real one?

Advisory checks produce information a human reads (a size comment, a preview URL) and nothing depends on their exit code. A real check validates a build or runs tests. If a non-required check does real validation, treat it as load-bearing, speed it up, don't drop it.

### What if I can't see the repo's required-check list?

Then required status is unknown, and you should not de-scope. Keep the check as-is (or make its output async) rather than risk removing something that actually gates the merge.

<a id="sources"></a>

## Sources

1. [GitHub Actions - pull_request activity types](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows)
2. [GitHub - troubleshooting required status checks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks)
