---
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-06

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.

## 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
on:
  pull_request:
    types: [opened, ready_for_review]   # note: no "synchronize"
```

## 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
```

## 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.

## 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.

## 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]` with no `synchronize`), 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.

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.

## 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.

## 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)

## 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)
