Quality gates — configuring thresholds

check_quality_gates reads the quality_gates section of .trace-mcp.json (or .trace.json). By default, with no gates configured, the MCP tool returns NO_GATES_CONFIGURED with an advisory warning pointing to this documentation — never a silent or misleading PASS in CI. Pass use_default_gates: true to explicitly opt into the conservative built-in ruleset, or define quality_gates.rules in your project config. The second half of this page is this repo’s own configuration as a worked example of how the numbers get chosen.

What runs when you configure nothing

With no quality_gates config and no inline rules:

Rule Threshold Severity
max_cyclomatic_complexity 30 error
max_circular_import_chains 0 error
max_coupling_instability 0.9 warning
Rule Threshold Severity
max_cyclomatic_complexity 30 warning
max_circular_import_chains 0 error
max_security_critical_findings 0 error
max_tech_debt_grade D warning

The remaining rules are only checked once you name them — an absent rule is not a zero threshold, it is no check at all. There is no coverage rule: coverage is not one of the signals this gate reads.

Configuring your own

{
  "quality_gates": {
    "enabled": true,
    "fail_on": "error",
    "rules": {
      "max_cyclomatic_complexity": { "threshold": 60, "severity": "warning" },
      "max_dead_exports_percent": { "threshold": 10, "severity": "warning" },
      "max_security_critical_findings": { "threshold": 0, "severity": "error" }
    }
  }
}

The eight rule keys:

Key Threshold means
max_cyclomatic_complexity highest cyclomatic complexity of any indexed symbol
max_coupling_instability highest instability (0–1) of any module
max_circular_import_chains number of circular import chains
max_dead_exports_percent share of exports nothing imports
max_tech_debt_grade worst module grade allowed, A–F
max_security_critical_findings critical findings from scan_security
max_antipattern_count findings from detect_antipatterns
max_code_smell_count findings from the code-smell scan

Every rule takes the same fields: threshold (a number, or a letter for the grade), severity (error or warning) and an optional message shown when it trips. fail_on decides what turns into a failing run — error (default), warning to fail on both, or none to report without failing. enabled: false turns the whole gate off.

Start by running check_quality_gates with the defaults, then raise only the rules your codebase legitimately trips — the worked example below is that process on this repo.

Worked example: this repo’s own thresholds

The values below live in this repository’s .trace-mcp.json. They are calibrated against the codebase’s actual, reviewed state — re-derive them after any large refactor round rather than treating them as permanent.

max_cyclomatic_complexity: 130 (warning)

The generic default of 30 fires on nearly every language/framework plugin in this repo — dispatch tables for 81 languages and 88 frameworks are inherently branchy. After decomposing every class that was a genuine god-object (DecisionStore 262→96, TopologyStore 125→49, IndexingPipeline 137→115, plus tool-gate/api-routes/memory-tools splits), the remaining top offenders are all language/framework plugins whose complexity is domain-necessary, not accidental: DartLanguagePlugin (123), FastAPIPlugin (119), CppLanguagePlugin (117), LaravelPlugin (105), ObjCLanguagePlugin / OcamlLanguagePlugin (103). 130 sits just above that accepted ceiling — today’s classes pass silently, but a genuinely new outlier above it still trips the warning.

max_security_critical_findings: 2 (error)

scan_security is a regex-based scanner with no data-flow analysis (a full taint/dataflow rewrite was explicitly scoped out — see comparisons). It cannot see past a runtime-validated identifier or a value’s closed provenance. Two findings are reviewed and accepted as false positives, not suppressed silently:

The threshold is 2, not 0, so a third critical finding — new or otherwise — still fails the gate and forces review. If either of these two findings’ code changes such that the value could become externally influenced, re-audit before assuming the finding is still a false positive.

max_circular_import_chains: 0 (error), max_tech_debt_grade: D (warning)

The circular-import rule is left at its default of 0 — no evidence-based reason to relax it. The tech-debt grade is not on by default at all; D is this repo’s own ceiling, set so a module sliding to F fails the run.

The gates are read by check_quality_gates, get_tech_debt and get_circular_imports (tools reference); where the file itself lives and how per-project overrides merge is in configuration.

Last updated: September 9, 2026