ai newsDi., 27. Okt.
EilmeldungUK Politician Jess Asato Got Deepfaked. Now She’s Taking xAI to Court

Kill-Matrix Pruning: Cutting the Golden Cases That Never Catch an AI C++ Patch

A team running an AI patch pipeline once showed me their nightly eval: 214 golden cases, a clean green dashboard, and a production regression that the suite had walked straight past. The postmortem took an hour. Then someone asked the obvious question, and nobody had an answer: how many of those 21

dev.toDienstag 13:48 · 27.10.7 Min. Lesezeit
Bild: dev.to

A team running an AI patch pipeline once showed me their nightly eval: 214 golden cases, a clean green dashboard, and a production regression that the suite had walked straight past. The postmortem took an hour. Then someone asked the obvious question, and nobody had an answer: how many of those 214 cases had ever failed for a reason worth knowing?

We instrumented the suite instead of arguing about it. Sixty-one cases had never failed on any patch the pipeline had ever scored. Thirty-three failed on nearly every patch, which made them noise rather than signal. The suite looked thorough and was mostly ceremony.

This article describes the instrument we built: a kill matrix. It is a small, reproducible way to find out which golden cases actually earn their runtime, and which ones you are paying for out of habit. A free-tier model and a free-tier runner make the measurement cheap enough to run weekly, but the method stands on its own.

A kill matrix needs a fixed oracle side. That side is a set of known-wrong C++ patches, called mutants, seeded by hand or by a mutation tool. The rule: every mutant must be a bug a reviewer would reject in a real PR.

Start with twenty. Small, boring, semantics-bearing mutations work best:

// original std:: size_t clamp_index ( std:: size_t i, std:: size_t n ) { return i < n? i: n - 1; } // mutant M07: boundary flipped, one-past-end reachable std:: size_t clamp_index ( std:: size_t i, std:: size_t n ) { return i <= n? i: n - 1; } // mutant M12: unsigned underflow when n == 0 std:: size_t clamp_index ( std:: size_t i, std:: size_t n ) { return i < n? i: n - 1; // unchanged, but n == 0 now allowed by caller } Enter fullscreen mode Exit fullscreen mode Record each mutant as a patch file plus a one-line description of the defect. Do not include anything you cannot defend in a code review. Style-only mutants produce a matrix full of false kills.

The matrix is a two-dimensional table: mutants down the rows, golden cases across the columns. Each cell answers one question — did this case fail when it saw this mutant?

The runner below is deliberately dumb. It compiles the mutant, applies the case, and writes a boolean. No model, no grader, no rubric. The point of this pass is to measure the cases, not the model.

# build_kill_matrix.py import json, pathlib, subprocess, sys ROOT = pathlib. Path ( __file__ ). parent MUTANTS = sorted (( ROOT / " mutants " ). glob ( " *.patch " )) CASES = sorted (( ROOT / " cases " ). glob ( " case_*.py " )) def run_case ( case: pathlib. Path ) -> bool: """ Return True when the case FAILS (i.e. the mutant is killed).

""" proc = subprocess. run ( [ sys. executable, str ( case )], cwd = ROOT / " build ", capture_output = True, timeout = 120, ) return proc. returncode!= 0 matrix = {} for mutant in MUTANTS: subprocess. run ([ " git ", " apply ", str ( mutant )], cwd = ROOT, check = True ) subprocess. run ([ " cmake ", " --build ", " build ", " -j " ], cwd = ROOT, check = True ) row = {} for case in CASES: row [ case. stem ] = run_case ( case ) matrix [ mutant. stem ] = row subprocess. run ([ " git ", " checkout ", " -- ", ". " ], cwd = ROOT, check = True ) ( ROOT / " kill_matrix.json " ). write_text ( json. dumps ( matrix, indent = 2 )) print ( f " { len ( MUTANTS ) } mutants x { len ( CASES ) } cases written " ) Enter fullscreen mode Exit fullscreen mode Two hygiene rules keep the matrix honest. First, freeze the compiler and flags for the whole run; a matrix built across two toolchains is two matrices stapled together.

Second, run each cell once and record timing, because a case that takes four minutes to kill one mutant is a different purchase than one that takes four seconds.

The raw JSON is not the deliverable. The classification is. Sort each case by how many mutants it kills, then look for the four shapes that matter.

The "never fires" bucket is where the silent regressions hide. A case with zero kills is either asserting something no realistic bug can violate, or it is silently skipping itself — a missing fixture, a disabled #ifdef, a test binary that swallowed a nonzero exit. Both need a human, not a deletion.

Once you know which cases kill which mutants, the minimal suite is a set cover problem. Greedy selection gets you within a constant factor of optimal and takes about thirty lines.

# prune_suite.py import json matrix = json. load ( open ( " kill_matrix.json " )) mutants = list ( matrix ) cases = list ( next ( iter ( matrix. values ()))) killers = { c: { m for m in mutants if matrix [ m ][ c ]} for c in cases } chosen, uncovered = [], set ( mutants ) while uncovered: best = max ( cases, key = lambda c: len ( killers [ c ] & uncovered ), default = None ) if best is None or not ( killers [ best ] & uncovered ): break chosen. append ( best ) uncovered -= killers [ best ] print ( f " kept { len ( chosen ) } / { len ( cases ) } cases; uncovered mutants: { sorted ( uncovered ) } " ) json. dump ( chosen, open ( " suite.json ", " w " ), indent = 2 ) Enter fullscreen mode Exit fullscreen mode Split the mutant set 70/30 before you run this. Prune on the 70 percent, then check how much of the 30 percent the reduced suite still kills. A pruned suite that drops below roughly ninety percent holdout coverage was fit to the mutants, not to the code.

This is where the harness meets infrastructure. The account's outreach brief points at two MonkeyCode options — free model access and a free server option — and both are relevant here for one reason: the kill matrix is a volume job. Twenty mutants across a hundred cases is two thousand executions, and the interesting question is not whether you can afford it once, but whether you can afford it every week.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Treat the free tier as an unverified budget until your own numbers exist. The brief cites a free token allowance; that figure is a marketing claim until a script counts what your sweep actually consumes. Log tokens per mutant, wall-clock per case, and failures per run, then compare against the allowance. If the ratio is comfortable, schedule the sweep weekly. If not, run it on the top twenty cases only and prune quarterly.

# one sweep, three numbers worth keeping /usr/bin/time -f "wall=%e s" python build_kill_matrix.py 2>> sweep.log grep -c '"true"' kill_matrix.json >> sweep.log wc -l sweep.log Enter fullscreen mode Exit fullscreen mode The same caution applies to the free server. Nothing in the brief says how long it stays up, how fast it builds, or whether it survives a -j build of your project. Measure those three things on day one, and keep a fallback runner configured so a capacity surprise costs you one sweep, not one quarter.

Aggressive compression has a failure mode: the suite becomes fast, green, and blind. Three cheap guards prevent it.

Skip this method if your mutant set would be invented rather than harvested from real rejections — a matrix of synthetic bugs measures your imagination, not your suite. Skip it if you have fewer than roughly fifteen cases, because set cover over a tiny universe just renames the problem. And skip it if the eval runs on a shared cluster where a two-thousand-execution sweep would starve other jobs; the measurement is not worth the queue.

What remains is a cheap habit. Once a quarter, you learn which of your golden cases still catch something, which never did, and what a full sweep actually costs on whatever free capacity you are leaning on. The dashboard stops being a number and starts being an argument.

Quelle: dev.to (dev.to). Original öffnen