A Rename Is the Quietest Delete

My second-opinion reviewer had written 438 reports, and not one of them flagged a removed assertion or a skipped test. Over a single week, my build agents’ commits had deleted assert and expect lines in four repositories and cut lines from a fifth one’s verification script. Weakening a test is one of the few things my workspace rules forbid outright, and the reviewer had read past every case.

It wasn’t hiding anything. It reads a big diff code first, and the test hunks sit at the bottom, where its attention ran out. So I stopped hoping it would get there. A small scanner now reads the diff before the reviewer does and puts four lists at the top of the prompt: assertions removed from test files, tests newly marked skip or only, test files deleted, and lines removed from a verify script or a test command. The reviewer has to rule on each item.

The scanner had to stay quiet when nothing was wrong, so it skips a removed line if the same text comes back elsewhere in that file. Tests get reshuffled all the time, and a moved assertion is still an assertion. It also decides which files count as tests by their path, the same way the test runner does.

The next review of that change came back blocking. The top finding was a diff that renames test/auth.test.mjs to src/auth.mjs and drops its assertions on the way. My scanner looked at the file’s new path, decided it was ordinary code, and never read the lines it removed. The reviewer couldn’t even run my suite (its read-only sandbox refused to create a temp file). It found the hole by reading.

Then I tried the cleanest version, a rename and nothing else. Git prints it like this:

diff --git a/test/auth.test.mjs b/src/auth.mjs
similarity index 100%
rename from test/auth.test.mjs
rename to src/auth.mjs

There are no minus signs, no removed assertions and no skips, and every line of the file survives. By my scanner’s own rule, nothing had been lost. In fact everything had. A test is a test only because the runner’s pattern finds it, and src/auth.mjs doesn’t match the pattern. The file still holds every assertion, and nothing will ever execute them again. Of all the ways to weaken a test, this one deletes it most completely, and it’s the one a diff makes look smallest.

My moved-lines rule was right about lines and wrong about files. A line keeps its meaning when it moves within a test file. A test file loses all of its meaning when it moves out of the tests, because the meaning was its location.

The fix judges every removed line by the path it left, not the path it arrived at. A test renamed out of the tests now goes on the deleted list next to the files that were removed outright, with a note saying where it went.


← all writing