gateViolations method

List<GateViolation> gateViolations({
  1. required Map<String, Confidence> confidenceByRule,
  2. Map<String, double> heuristicPrecisionThresholds = const {},
  3. CorpusMetrics? baseline,
  4. double recallTolerance = 0.02,
})

Evaluates the CI gate. Returns every breach; an empty list means the gate passes.

It fails when: a deterministic rule has precision below 1.0; a heuristic rule has precision below its entry in heuristicPrecisionThresholds; any rule has a false positive on a clean case; or a rule's recall regressed more than recallTolerance below baseline.

Implementation

List<GateViolation> gateViolations({
  required Map<String, Confidence> confidenceByRule,
  Map<String, double> heuristicPrecisionThresholds = const {},
  CorpusMetrics? baseline,
  double recallTolerance = 0.02,
}) {
  final violations = <GateViolation>[];
  for (final metrics in rules) {
    final id = metrics.ruleId;
    final confidence = confidenceByRule[id] ?? Confidence.deterministic;

    if (metrics.falsePositivesOnClean > 0) {
      violations.add(
        GateViolation(
          id,
          GateViolationKind.falsePositiveOnClean,
          '${metrics.falsePositivesOnClean} false positive(s) on clean cases.',
        ),
      );
    }

    if (confidence == Confidence.deterministic) {
      if (metrics.precision < 1.0) {
        violations.add(
          GateViolation(
            id,
            GateViolationKind.deterministicPrecision,
            'deterministic precision is '
            '${metrics.precision.toStringAsFixed(3)} (< 1.0): '
            '${metrics.falsePositives} false positive(s).',
          ),
        );
      }
    } else {
      final threshold = heuristicPrecisionThresholds[id];
      if (threshold != null && metrics.precision < threshold) {
        violations.add(
          GateViolation(
            id,
            GateViolationKind.heuristicPrecision,
            'heuristic precision is '
            '${metrics.precision.toStringAsFixed(3)} '
            '(< declared ${threshold.toStringAsFixed(3)}).',
          ),
        );
      }
    }

    final priorRecall = baseline?.byRule[id]?.recall;
    if (priorRecall != null &&
        metrics.recall < priorRecall - recallTolerance) {
      violations.add(
        GateViolation(
          id,
          GateViolationKind.recallRegression,
          'recall dropped to ${metrics.recall.toStringAsFixed(3)} from a '
          'baseline of ${priorRecall.toStringAsFixed(3)}.',
        ),
      );
    }
  }
  return violations;
}