Every ECO round fixes most of what it targets and disturbs a few paths that were passing with little margin. Early rounds look fast; later rounds crawl. A simple model shows why, and shows that late in closure, reducing the breakage rate helps more than fixing more.
v(next) = v * (1 - f) + b * N
f = fraction of current violations fixed per round
b = fraction of near-critical endpoints (N of them) broken per round
fixed point (stall level) = b * N / fdef run(f, b, n_near, v0, rounds):
v, out = v0, [v0]
for _ in range(rounds):
v = v * (1 - f) + b * n_near
out.append(v)
return out
def close(xs, ys):
return all(abs(x - y) < 0.01 for x, y in zip(xs, ys)) and len(xs) == len(ys)
a = run(0.7, 0.02, 300, 200, 7)
c = run(0.7, 0.005, 300, 200, 7)
assert close(a, [200, 66.0, 25.8, 13.74, 10.12, 9.04, 8.71, 8.61])
assert close(c, [200, 61.5, 19.95, 7.485, 3.746, 2.624, 2.287, 2.186])
floor_a, floor_c = 0.02 * 300 / 0.7, 0.005 * 300 / 0.7
assert round(floor_a, 2) == 8.57 and round(floor_c, 2) == 2.14
print("PASS: stall level", round(floor_a, 2), "with 2% breakage;", round(floor_c, 2), "with 0.5% breakage")Round | Violations, 2% breakage | Violations, 0.5% breakage |
|---|---|---|
0 | 200 | 200 |
1 | 66 | 61.5 |
2 | 25.8 | 20 |
3 | 13.7 | 7.5 |
4 | 10.1 | 3.7 |
5 | 9.0 | 2.6 |
6 | 8.7 | 2.3 |
7 | 8.6 | 2.2 |
With 300 near-critical endpoints, a fix rate of 70% and a breakage rate of 2%, the loop levels off at about 8.6 violations, not zero: each round fixes 70% of what is left but breaks 6 more. Cutting breakage to 0.5% lowers the stall level to about 2.1. The numbers are a model, not a prediction, but the shape is what teams see: rapid progress, then a long tail. Late in closure, the productive question is why fixes break things.
Closure is tracked as violations per round with the number of near-critical endpoints, so a floor is visible early.
Breakage is measured: new violations per ECO round, listed by cause.
When progress stalls, the response is a different kind of fix or a change of structure, not more of the same.
0.01 × 400 / 0.8 = 5 violations.