DEV Community

Jay_Stride
Jay_Stride

Posted on AI-assisted

Your Tests Pass. So Does the Wrong Code

A green test suite tells you one thing: the implementation you wrote satisfies the tests you wrote.

It doesn't tell you whether the version one small edit away would pass too. If swapping < for <= would have sailed through, the suite was never checking that boundary.

Coding agents make this sharper. They write the code and the tests from the same assumption, the suite goes green, and nobody stops to ask what else it would accept.

So I built TestSlop. Point it at a diff and it finds one nearby alternative implementation your tests also accept, then shows the input that tells the two apart.

Terminal demo: testslop twin reports ORIGINAL 3/3 and EVIL TWIN 3/3 tests passed, missing witness quantity = 1

One sentence: TestSlop finds one nearby "Evil Twin" implementation that behaves differently but still passes the same tests.

That GIF runs a three-line quantity check. As text:

ORIGINAL    quantity <= 0
EVIL TWIN   quantity <= 1

ORIGINAL    3 / 3 tests passed
EVIL TWIN   3 / 3 tests passed

Missing witness: quantity = 1
Enter fullscreen mode Exit fullscreen mode

Both passed. The two versions only disagree at quantity = 1, and no test covers that value.

What is an Evil Twin?

An Evil Twin is a nearby implementation that behaves differently but still passes the same tests.

The missing witness is the input that separates them. Here it is quantity = 1, the simplest input where the two versions give different answers.

TestSlop hands you the pair and the witness. Which version matches your contract stays your call.

A real agent workflow

Most of this repo's public face went through an agent workflow. This slice is the part that pushed the sanitized history to GitHub and checked the rendered README, then returned a release verdict:

Building the TestSlop DEV launch article openai
You

TestSlop — Private GitHub Push & Final Preflight

The privacy/history cleanup is complete.

Current sanitized commit:
text
163f0a4

The working tree is clean and the GitHub repository is currently private and empty.

Critical constraints

  • DO NOT invoke Claude CLI.
  • DO NOT publish to npm.
  • DO NOT make the GitHub repository public.
  • DO NOT create a GitHub Release.
  • DO NOT add features.
  • DO NOT rewrite history again unless a new privacy problem is actually discovered.

1. Confirm local state

Run:
bash
git status
git log --oneline --decorate -10
git rev-parse HEAD
git remote -v

Confirm the repository is clean and HEAD is the sanitized history.

2. Configure the private GitHub remote

Repository:
text
https://github.com/hyukvoid/TestSlop

If the GitHub repository still uses Testslop casing, use the actual current remote URL and report the mismatch. Do not rename the GitHub repository yourself.

Add:
bash
git remote add origin https://github.com/hyukvoid/TestSlop.git

or correct the existing origin if one unexpectedly exists.

3. Push only to the PRIVATE repository

Because the remote repository is empty, perform a normal initial push.
bash
git push -u origin master

Do not force-push unless GitHub unexpectedly contains history, in which case STOP and report it.

4. Verify the remote

After push, verify:
bash
git status
git remote -v
git log --oneline --decorate -5
git ls-remote origin

Confirm the pushed HEAD matches the sanitized local HEAD.

5. Inspect the GitHub-facing repository

Review the repository as it now exists remotely.

Check:

  • README renders correctly
  • hero is visible immediately
  • code fences/layout are intact
  • relative links work
  • historical ms example links work
  • LICENSE appears as MIT
  • package metadata points to the correct GitHub repository
  • research archive is present but does not dominate the first screen
  • no Windows absolute personal paths appear
  • no private Gmail address appears
  • no private notification topic appears
  • no secrets appear
  • no stale Jev claims appear
  • no npm-install claim suggests the package is already published

6. GitHub first-screen test

Judge the rendered README at:

3 seconds

Can the visitor see:

Your tests pass. So does the wrong code.

10 seconds

Can they understand:
text
ORIGINAL 3/3 passed
EVIL TWIN 3/3 passed

and why that matters?

30 seconds

Can they understand how to run the local demo?

60 seconds

Can they find the real ms example and the project's limitations?

Do not edit unless the rendered GitHub version exposes a concrete presentation problem.

7. Final release verdict

Return only one:
text
READY TO MAKE PUBLIC

or:
text
NEEDS ONE MORE FIX

If a fix is needed, explain exactly what it is.

Do not make the repository public.

The human will perform the public visibility change.

Agent

I’ll verify the sanitized HEAD and remote’s current privacy/emptiness first. If it is still private and empty, I’ll add the verified origin, make a normal master push, then inspect the remote README and release-facing files without changing visibility.

Agent
Agent

The local tree is clean at the expected sanitized commit. GitHub confirms the repository is private, its canonical casing is hyukvoid/TestSlop, and git ls-remote returned no refs, so I’m adding that origin and making the requested normal initial push.

Agent
Agent
Agent
Agent

GitHub now tracks the exact sanitized HEAD, and the working tree is clean. I’m checking GitHub’s rendered README and its linked files next; I’ll use the computer-use skill for the first-screen view, then separately verify the repository files and privacy strings through the authenticated API.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

GitHub’s rendered README preserves the opening slogan, complete hero output, and all code fences; the live default branch is now master, and GitHub recognizes the MIT license. I’m doing the final remote link/archive/release checks and confirming privacy stayed enabled after the push.

Agent
Agent
Agent
Agent

READY TO MAKE PUBLIC

How it works

Flow diagram: agent changes code, tests pass, TestSlop creates an Evil Twin, runs the same tests, both pass, shows the missing witness

The diagram is the whole loop: shift one boundary comparison, rerun your tests against the alternative in a scratch copy, and print both results with the witness. Your source tree stays untouched.

A real run against vercel/ms

Terminal capture of the vercel/ms replay: ORIGINAL 163/163 passed, EVIL TWIN 163/163 passed, missing witness exactly one year, 1y becomes 12mo

One fixture in the repo replays a real ms commit, the one that added month formatting, against its preserved test suite.

The original and the Evil Twin (msAbs >= y changed to msAbs > y) both passed 163 of 163 tests. The missing witness is exactly one year: at 31,557,600,000 ms the output moves from 1y to 12mo.

The formatter tests checked one millisecond past a year and never exactly one year. One historical example, replayable offline. It doesn't show that TestSlop catches shipped bugs in general.

What it doesn't do

TestSlop is narrow on purpose. It looks at JavaScript and TypeScript boundary comparisons on changed lines, so plenty of diffs produce no Twin at all.

  • No Twin found doesn't mean your code is correct. It can mean the tests are tight, or the diff is a shape TestSlop doesn't handle.
  • A Twin passing isn't automatically a bug. It's evidence your suite accepts that behavior too.
  • Tests run with your normal user permissions in a scratch copy. That protects your source; it isn't a security sandbox.
  • It doesn't replace general-purpose mutation testing. Different tool, different question.

Try it and tell me what breaks

The code is on GitHub: github.com/hyukvoid/TestSlop

It runs from a local build (npm install, then npm run demo). It isn't published to npm. The repo keeps the research notes from the three phases that narrowed the project down to this idea.

If you've used mutation testing or coding-agent workflows, I'd be curious whether one concrete counterexample feels more useful than a score.

Top comments (3)

Collapse
 
mihai_leanzero profile image
Mihai Perdum •

This is a nicely scoped idea. Flipping the frame from "here's a mutation score" to "here's one concrete counterexample and the input that tells it apart from your code" is a much more actionable result than most mutation testing output I've dealt with. The ms example is a good pick too, since it's a real boundary nobody happened to write a test for.

One thing I'm curious about: the README says Twin selection is deterministic and the default command shows at most one Twin per run. When a diff touches more than one boundary comparison that could each spawn a Twin on its own, what decides which one gets reported first, first occurrence in the diff, or something like shortest distance to the witness? Trying to figure out whether re-running after fixing the first Twin is the intended way to surface the rest, or whether verify shows all the candidates at once.

Collapse
 
jay_stride profile image
Jay_Stride •

Yeah, that's basically the intended loop. Block the witness with a test, rerun, and the next surviving Twin comes up.
On the ordering question: it's not shortest distance to the witness. Surviving credible Twins get sorted deterministically, by alternative priority first, then file path, line number, and source start offset. Same diff in, same first Twin out every time, which is what I cared about most when picking that order.
verify is the escape hatch when you want several candidates at once. It runs multiple alternatives and shows their results, though it's bounded too: default budget of 12, and it stops after 4 unverified results. --budget and --stop-after move those if you need to push further.
Thanks for actually reading the README and the selection code, by the way. The "one concrete counterexample" framing you described is basically the job I want the tool to do, and your question landed on a real gap: the README never spells out the selection order or the fix-then-rerun loop. I should write that in properly.

Collapse
 
maxpaint profile image
Maksym Danylenko •

The vercel/ms example is a great illustration — 163/163 on both. It's also why agent-generated tests can't catch this kind of bug: they're written from the current code, so they pin whatever it does, wrong year included. Even the unit tests skills we use make tests stronger, not psychic — the expectation has to come from somewhere other than the implementation.