Skip to main content
Back to blog

Critiq team

  • guides

Inline PR comments from Critiq: dedupe, severity, and noise control

Use critiq-action for inline PR comments: dedupe across reruns, fail-on-severity gates, and when to block merge vs comment only.

Inline PR comments from Critiq: dedupe, severity, and noise control content

Pull request review works best when feedback sits on the line you changed, not buried in a log file three tabs away. The Critiq GitHub Action runs critiq check on your PR diff and posts inline review comments so rule-backed findings show up where authors already look. That only helps if reruns do not spam the thread, severities map to merge policy, and you can tell when a comment is enough versus when the job should fail.

This guide walks through a practical setup: add the action, read how deduplication keeps PRs quiet across pushes, tune fail-on-severity for merge gates, and reduce noise when comments alone are the right signal. Everything here uses the open source critiq-dev/critiq-action composite on GitHub Actions. You do not need a Critiq Cloud account or a paid product.

If you already run critiq check locally, the Action is the same engine with GitHub-native delivery. The difference is where results land: terminal and JSON on your machine, inline threads and optional check failure on the PR.

What the action does on a pull request

On pull_request, the action installs @critiq/cli (from your package.json when declared, otherwise a pinned npm prefix install), runs critiq check against the PR base and head SHAs, writes JSON to the runner, and, unless you turn comments off, posts GitHub review comments on flagged lines that appear in the diff. GitHub only allows inline comments on diff lines; Critiq's default diff-scoped scan matches that constraint.

After the scan and comment step, a separate fail-on-severity step can fail the job when findings meet your threshold. Comments still post when comment-mode is enabled, even if the job ends red. That ordering matters: authors see feedback on the diff before merge policy blocks them.

Start with a minimal workflow

Create .github/workflows/critiq.yml in the repository you want to scan. Checkout needs full history so diff mode can resolve base and head; the job needs pull-requests: write when you use inline comments.

name: Critiq

on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  critiq:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Critiq
        uses: critiq-dev/critiq-action@v1
        with:
          fail-on-severity: off

Open a pull request and confirm the workflow on the Actions tab. With defaults (comment-mode: inline, fail-on-severity: off), you get review comments on the diff and a green job unless critiq check itself errors. Pin @v1 for a major tag or a commit SHA if your org requires supply-chain pinning. Full input reference: https://github.com/critiq-dev/critiq-action#inputs

Inline comments and comment-mode

comment-mode controls how findings reach GitHub. The default inline posts one review comment per finding on the PR head commit, on the file and line from the JSON report when that line is in the PR diff.

  • inline (default), review comments on the diff only
  • inline+summary, inline comments plus one sticky issue comment with finding counts
  • off, scan and outputs only; no GitHub comments (useful for SARIF or custom transformers)

Each Critiq comment includes the rule id, severity, message, and remediation text from the catalog, the same fields you see locally in pretty or JSON output. That keeps PR feedback aligned with critiq check on your laptop.

      - name: Run Critiq
        uses: critiq-dev/critiq-action@v1
        with:
          comment-mode: inline+summary
          fail-on-severity: off

How deduplication works

Without dedupe, every push would add duplicate comments for the same finding, and resolved threads would reappear after a fix-and-push cycle. critiq-action applies four rules so reruns and human resolves do not spam the PR.

  • Marker, Each comment embeds a hidden HTML marker tied to fingerprints.primary from the scan JSON. That stable fingerprint is the identity of the finding across runs.
  • Same line, If any review comment already exists on the same path + line at the PR head commit, Critiq does not add another comment there.
  • Resolved threads, If that fingerprint appears in a resolved review thread (GraphQL), Critiq skips posting that finding again.
  • Open threads, If the fingerprint already exists in an unresolved thread, the finding is skipped rather than duplicated.

Practical effect: when an author fixes an issue and resolves the thread, a later push does not resurrect the same comment. When two rules would fire on one diff line, you still get at most one comment on that line per head commit. When a finding cannot be placed on the diff (no line in the PR patch), it is skipped for inline posting, not silently dropped from the JSON report.

The action exposes review-comments-created and review-comments-skipped outputs after the post step. Skipped counts include dedupe hits and findings missing location or fingerprint data. Use them in a follow-up step when you are tuning noise:

      - name: Run Critiq
        id: critiq
        uses: critiq-dev/critiq-action@v1

      - name: Comment stats
        run: |
          echo "Created: ${{ steps.critiq.outputs.review-comments-created }}"
          echo "Skipped: ${{ steps.critiq.outputs.review-comments-skipped }}"
          echo "Findings: ${{ steps.critiq.outputs.finding-count }}"

Severity gating with fail-on-severity

Inline comments inform; fail-on-severity enforces. The input runs after scan and post, and fails the job when any finding is at or above the level you choose. Severity order is low → medium → high → critical; each threshold includes that level and every more severe one.

  • off (default), never fail for finding severity; comments still run when enabled
  • low, fail on low, medium, high, or critical
  • medium, fail on medium, high, or critical
  • high, fail on high or critical only
  • critical, fail only on critical findings

Platform teams often start with fail-on-severity: off so authors learn the catalog without blocked merges, then move to high once high-severity rules are trusted. Security-sensitive repos may use medium for a stricter gate. The exit code from critiq check still reflects the scan; fail-on-severity is an additional policy layer on top of the JSON findings.

      - name: Run Critiq
        uses: critiq-dev/critiq-action@v1
        with:
          fail-on-severity: high
          comment-mode: inline

Reducing noise without turning Critiq off

Noise on a PR is usually a policy problem, not a comment-format problem. These levers keep signal high while staying on deterministic rules.

Tune the catalog in the repo

Add .critiq/config.yaml when you need stricter presets, path ignores, or catalog tuning. The action loads it automatically from working-directory. Disabling a noisy rule by id is preferable to muting the whole check, the rule id in the comment matches what you change in config. CLI reference: https://github.com/critiq-dev/critiq-core/blob/main/docs/reference/cli.md

Scope monorepo packages

In a monorepo, set working-directory to the package root where dependencies install and target to the subtree you want analyzed (for example apps/web). That limits findings to the surface area the PR actually touches, which cuts unrelated comments on unchanged packages.

      - name: Run Critiq
        uses: critiq-dev/critiq-action@v1
        with:
          working-directory: apps/web
          target: apps/web
          fail-on-severity: high

Choose summary vs inline-only

inline+summary adds one sticky issue comment with counts. Some teams like a single rollup for managers; others find it redundant with inline threads. If the issue comment feels noisy, stay on inline and rely on Actions logs plus finding-count.

Pin CLI and rules versions

When the root package.json does not declare @critiq/cli, the action installs cli-version and rules-version (default latest) under RUNNER_TEMP. Pin semver tags in the workflow when you want CI to match a known catalog release and avoid surprise new rules on unrelated PRs.

        with:
          cli-version: "0.9.0"
          rules-version: "0.9.0"
          fail-on-severity: high

When to use comments vs a failing check

Comments and check failure solve different problems. Use both deliberately.

  • Comments only (fail-on-severity: off), Feedback for authors without blocking merge. Good for adoption, experimental rules, or repos where another policy gate already exists.
  • Comments + soft gate (medium or high), Inline context on the diff plus a red check when severity crosses your bar. Authors see why before the branch protection rule fires.
  • Check without comments (comment-mode: off), Machine-readable json-path and finding-count for dashboards, SARIF pipelines, or custom bots. Pair with fail-on-severity when humans do not need GitHub-native threads.
  • Separate secret gate, critiq-action runs critiq check, not critiq audit secrets. If credential-shaped literals must block merge, add a dedicated audit secrets step; do not assume check exit code alone enforces secrets.

Branch protection should reference the Critiq job name you use in the workflow. If fail-on-severity is off, a green Critiq job means "no scan failure," not "zero findings." Inspect finding-count and the JSON artifact path when you need zero-findings policies without changing fail-on-severity.

Reusable workflow for many repos

Org-wide consistency is easier with the reusable workflow shipped in critiq-action. Caller workflows stay small; you centralize checkout depth, permissions, and inputs once.

name: Critiq

on:
  pull_request:

jobs:
  critiq:
    uses: critiq-dev/critiq-action/.github/workflows/reusable-critiq.yml@v1
    secrets: inherit

Pass the same inputs as the composite action (cli-version, rules-version, working-directory, target, comment-mode, fail-on-severity) through the reusable workflow inputs when you need overrides. The reusable job checks out only the caller repository, no Critiq Cloud clone. Repositories that depend on a local critiq-core file: dependency should use a custom workflow that builds core first; see the critiq-action README monorepo and reusable sections.

Troubleshooting common symptoms

  • No review comments, Confirm pull-requests: write, comment-mode is not off, and findings map to lines in the PR diff. Secrets-only paths outside the diff will not get inline comments.
  • Duplicate comments after dedupe, Usually a new fingerprint (rule or engine change) or a different path/line. Compare fingerprints.primary in json-path output across runs.
  • Job fails unexpectedly, Lower fail-on-severity, set it to off temporarily, or fix findings; read exit-code and finding-count outputs.
  • Diff scan misses files, Use fetch-depth: 0 on checkout so base and head SHAs exist locally.
  • Comments but you need SARIF, Set comment-mode: off and consume json-path in a follow-up step or run critiq check --format sarif yourself.

Privacy and what runs where

Scanning runs on your GitHub Actions runner in your account. Critiq does not operate a backend that receives your source or scan JSON from this action. GITHUB_TOKEN is used only to post PR comments when comment-mode allows it, within the permissions you declare. npm is contacted to install @critiq/cli and @critiq/rules (or your declared dependencies). Details: https://github.com/critiq-dev/critiq-action/blob/main/PRIVACY.md

Next steps

  • GitHub Action repo and README, https://github.com/critiq-dev/critiq-action
  • Marketplace listing, search critiq-dev/critiq-action on GitHub Marketplace
  • OSS CLI and rules, https://critiq.dev/products/oss
  • Documentation, https://docs.critiq.dev/
  • GitHub Actions integration page, https://critiq.dev/integrations/github-actions

Start with inline comments and fail-on-severity: off, watch review-comments-skipped on a few PRs, then raise the severity gate when the team trusts the catalog. Dedupe and severity are the difference between a bot that nags and a check that behaves like infrastructure you operate, the kind your reviewers actually resolve instead of ignoring.