What 'Good Enough to Ship' Actually Means
On the LLM Gym's promotion gate — four pass-or-fail checks, no partial credit — and why a single weighted score was too easy to talk myself into.
The gym is a repurposed machine that sits in a closet and fine-tunes small language model adapters against a rotating set of tasks. Every few days it produces a candidate that thinks it deserves to replace whatever is currently serving live traffic. Most of them don't get the promotion, and I've made peace with that.
The rule I settled on, after enough false starts to be embarrassed about, is that a candidate has to clear four separate checks before it goes live, and it has to clear all four. It needs to beat its own starting point, meaning the base model before any fine-tuning touched it, because a tuned model that ends up worse than doing nothing doesn't count as progress just because I spent the compute on it. It needs to beat the current champion head-to-head on a test set that never changes, so I can't quietly make the exam easier when a candidate is struggling. It has to clear a security and robustness pass. And it can't have gotten more trigger-happy about refusing ordinary, benign requests than the model it's replacing, a pettier failure mode than it sounds, and one I only started checking for after a candidate got safer by getting less useful.
None of those four is optional, and none of them can cover for another one. That's the part that took me longest to actually commit to.
Why not just add up the scores
For a while I ran something closer to a weighted composite: security counted for some percentage, task performance for the rest, refusal behavior as a minor adjustment. It felt more sophisticated. It also meant a candidate that was noticeably better at the core task could out-vote a real regression somewhere else in the model, and the composite would wave it through with a number that looked like progress.
I've written before about the general shape of that problem: a scoring setup that inverts under some condition, or averages away exactly the signal you needed to catch, and you don't find out until something's already live and behaving worse than the thing it replaced. That lesson generalizes past the specific bug that taught it to me. Any single number is an invitation to let one good column pay for a bad one, and when you're the only person reviewing the diff, that invitation is dangerous, because there's no second reviewer around to ask why the security number dipped while everything else went up.
An all-or-nothing gate removes that trade entirely. A candidate that's meaningfully smarter but slightly less safe doesn't ship. A candidate that's safer but flatly worse than the base model doesn't ship either. It's a blunter instrument than a weighted score. Blunt is harder to argue yourself around at eleven at night when a run just finished and you want it to be done.
Most nights the gate isn't stopping some hypothetical adversary from gaming the metrics. It's stopping me, tired, looking at three out of four green checks and one amber one, telling myself the amber one is close enough to round up. It never is. That's usually the one metric I didn't feel like re-running.
Running this solo means there's no one to catch me lowering the bar except the bar itself. So the bar has to be dumb and rigid rather than smart and negotiable. I don't get partial credit for effort, and neither does the model. A version either earns the promotion on every axis I said mattered, or it goes back in the training queue with the rest of the also-rans, and the champion keeps serving traffic until something actually beats it, not just something that mostly does.
Most weeks that means nothing ships. I've made my peace with that part too.