Skip to content

ADR-0008: Block merges on metric regressions with a CI gate

  • Status: Accepted
  • Date: 2026-08-31
  • Deciders: Maintainers

Context

The curriculum's central claim is build the measurement before the improvement. A repository that teaches that while merging changes without measuring them is not credible.

Options considered

Option A — trust the PR template

Ask for a measurement table and rely on reviewers. Costs: reviewers do not run evaluations. The table gets filled in with a point estimate and no interval, or left empty, and after three months nobody notices.

Option B — report only, never block

Post a scorecard, let humans decide. Costs: a warning everyone learns to scroll past. Also teaches that a gate is advisory, which is the opposite of the section-6 lesson.

Option C — a hard gate with a documented override

Decision

Option C. scripts/run_eval.py compares against a committed baseline and exits non-zero on a regression beyond tolerance. The workflow posts the scorecard on the PR either way. Re-baselining is a deliberate act (--baseline) that must happen in the same PR, so the diff shows both the change and the new numbers.

Consequences

Good. A number that changes is visible in the PR without anyone running anything, which is the single highest-leverage habit in the repo. Deliberate changes are possible but leave a trace — the baseline diff is in the commit history, so "when did full-chain recall drop?" is answerable by git log. Students experience a real release gate rather than reading about one.

Bad. The gate is slower than the rest of CI (~2 minutes) and runs on every nanorag/ change. Tolerances (2–3 points) are judgement calls that will occasionally block a legitimate change and occasionally let a small real regression through — they are a compromise between false blocks and false passes, and we picked them by looking at the noise band rather than by principle. And the baseline is a single committed file, so two PRs re-baselining concurrently conflict; that is annoying and also correct, because they should not both be changing the number silently.

Revisit when: the eval set grows enough that the noise band shrinks. Tolerances should shrink with it, and someone has to remember to do that — which is itself an argument for recording the noise band in the baseline file.

What would change this

A false-positive rate high enough that people route around it.

A gate that blocks correct changes gets disabled, and a disabled gate is worse than none because it still looks like a control. The number to watch is how often the gate is overridden: if more than roughly one in ten blocked PRs is merged anyway after review, the threshold is wrong — most likely the noise band is too tight for the eval-set size, which is the power problem.

The fix in that case is to grow the eval set, not to loosen the gate.