Skip to main content

Thresholds

A threshold is the maximum number of findings a gate will tolerate before it fires. It gives you precise control over what counts as a failure and what is acceptable noise.

What a threshold does

When a scan completes, Gatekeep evaluates each gate in your contract:

  1. Filter findings to the gate's category.
  2. Keep only findings at or above the gate's severity.
  3. Count those findings.
  4. If the count exceeds threshold, the gate fires.

A gate with "threshold": 0 fires as soon as a single qualifying finding exists. A gate with "threshold": 3 tolerates up to three qualifying findings before firing.

Default threshold

The default value is 0. If you omit threshold from a gate, any matching finding will cause the gate to fire. This is the strictest option and is appropriate for security gates in production services.

Choosing a threshold

Use threshold values to reflect the reality of your codebase and your team's capacity to act:

Scenario Recommended threshold Reasoning
Zero-tolerance security gate on a new service 0 Any critical or high security finding must block.
Security gate on an existing service with known tech debt 2 or 3 Allows some pre-existing findings while blocking new ones during remediation.
Quality gate (complexity, type hints) in a large legacy codebase 10 or higher Prevents the gate firing on every PR while the codebase is being improved incrementally.
Governance gate (missing README, no CI) 0 These are binary conditions; one is too many.
License gate during a compliance audit 0 Copyleft findings in a commercial repo require immediate review.

Threshold examples

Zero threshold (strictest)

{
  "category": "security",
  "severity": "critical",
  "threshold": 0,
  "blocking": true
}

Fires immediately if any critical security finding is present. Blocks merge when "blocking": true.

Permissive threshold for a work-in-progress service

{
  "category": "code_quality",
  "severity": "low",
  "threshold": 15,
  "blocking": false,
  "description": "Quality gate in progress; non-blocking while team addresses tech debt"
}

Fires only when quality findings exceed 15. Non-blocking, so the scan result is informational.

Graduated approach: warn early, block late

{
  "gates": [
    {
      "category": "dependency",
      "severity": "high",
      "threshold": 0,
      "blocking": true,
      "description": "Block on any high or critical dependency CVE"
    },
    {
      "category": "dependency",
      "severity": "medium",
      "threshold": 5,
      "blocking": false,
      "description": "Warn when medium CVEs accumulate"
    }
  ]
}

Two gates on the same category with different severity and blocking settings. The first blocks on any high-or-above CVE. The second warns when medium CVEs reach five, without blocking.

Threshold and the blocking flag

The threshold field controls whether the gate fires. The blocking field controls what happens when it fires.

Gate fires? blocking Outcome
No Either Gate passes. No action required.
Yes true Scan result is failed. CI integration exits non-zero.
Yes false Gate fires but scan result is passed_with_warnings. CI integration exits zero.

Start with "blocking": false when introducing a new gate to an existing service. Observe how often it fires over a few weeks, adjust the threshold to a realistic baseline, then switch to "blocking": true.

Reducing threshold over time

One effective pattern for addressing legacy tech debt is ratcheting: start with a high threshold that reflects the current state of the codebase, then lower it incrementally as findings are resolved. The contract in version control records your progress.

{
  "description": "Ratchet: started at 20, target is 0 by end of Q3",
  "category": "code_quality",
  "severity": "medium",
  "threshold": 8,
  "blocking": true
}

Update threshold in each sprint as the team resolves findings. Merge the updated contract before reducing it so CI does not fail on the existing codebase.

Next steps