Yes, for these tasks, and that is less of a win than it sounds. We wrote a program for each of six repair and conversion jobs and ran it through the 135 checks we had built for AI models. The programs passed all 135, the same as Claude Sonnet with our written rules loaded. Then a sentence that was in none of the cases showed one rule refusing perfectly good text.

Method

We picked six jobs where each input has one right answer that can be written down as rules: undoing mojibake (text whose bytes were read in the wrong encoding), repairing a broken CSV file, repairing broken JSONL, repairing a Markdown table, converting a cron schedule between five scheduler dialects, and writing Bulgarian in Latin letters under the official system.

Each job already had a test: between 21 and 24 cases, fixed before any model saw them, each with a check written in code. Days earlier, Claude Sonnet and Claude Haiku had answered every case once, from the bare request and again with our rules for that job loaded, with tools switched off.

This time there was no model. Each program is ordinary TypeScript with no outside library: text goes in, a result or a one-line refusal comes out. We fed the programs the same 135 inputs and scored their output with the same checks. Every test file also counts the cases it ran against the number in the case file, breaks one output on purpose to prove the checks can fail, and throws 60 KB of hostile input at the program, such as ten thousand open braces. A second reviewer then broke the programs and the payment code 21 times on purpose; each break made a named test fail.

Results

Job Cases Sonnet, bare → rules Haiku, bare → rules Program
Mojibake 21 16 → 21 16 → 20 21
CSV 23 16 → 23 17 → 22 23
JSONL 22 15 → 22 15 → 22 22
Markdown table 21 17 → 21 17 → 20 21
Cron dialects 24 20 → 24 12 → 24 24
Bulgarian to Latin 24 15 → 24 11 → 18 24
All six 135 99 → 135 88 → 126 135

The models needed the rules, and the rules were enough for Sonnet. From the bare request, Sonnet got 99 of 135 right and Haiku 88. Most of the misses were guesses: a mojibake text with two possible originals turned into one of them, a CSV repair that met a row carrying an extra field and decided on its own which two fields belonged together, a JSONL record cut off halfway returned as if it were whole. With the rules loaded, Sonnet got every case and Haiku 126.

The programs matched Sonnet with rules, case for case. On these 135 inputs there was nothing a model did that the code could not. The code also gives the same answer to the same input every time, needs no prompt and does not read a note hidden in the data as an instruction.

And then the first gap. The mojibake program had a rule from our skill: question marks between two letters mean the letters were lost, so refuse rather than guess. All 135 cases passed. Then we gave it "See https://example.com/page?id=1 for details." and it refused, calling a web address damaged text. "Is it?Yes, it is." was refused as well. The one case with question marks between letters had them in runs of two to four, in German words that lost their special letters. No case had a single question mark wedged between two letters, because the cases were built to catch a model's mistakes, and no model mistakes a web address for broken bytes. We changed the rule to two or more question marks between letters and added both sentences as a test. The price of the change is stated in the same test: a word that lost exactly one letter is no longer refused and passes through as it is.

The second gap came from breaking the program on purpose. The mojibake code ignores letter pairs that only happen to look like damaged bytes unless such pairs make up at least half of the characters outside plain ASCII. The reviewer removed that threshold and all the cases still passed. In correct Russian, the capitalised word for "her" is one such pair, so without the threshold a correct Russian sentence was refused as damaged. Again, no case contained one. Two Russian sentences are now tests.

Refusals stay refusals. Where the cron target cannot express a schedule exactly, the program says so in one line instead of sending something close. It never reads today's date, so the same request gives the same schedule next month.

The six programs run as pay-per-call APIs; the API notes list them. The skill files remain for agents that do the job themselves, for example mojibake-repair, csv-repair and jsonl-repair.

Writing the test

  • A case set built for a model is not a test of code. Cases aim at the places a model slips. A program slips in different places, usually on plain input that nobody thought to include.
  • Add ordinary inputs. A web address, a question in a sentence, correct text in another alphabet. A repair tool's worst mistake is changing or refusing text that was fine.
  • Break the code on purpose. Remove a rule and run the tests. If nothing fails, no test needs that rule, and you do not know what it protects.
  • Say what a fix costs. Each change to a refusal rule moves some inputs from refused to returned unchanged. Write which ones into the test.

What we did not measure

  • Running each case again. Each model saw each case once. The program's answers do not change between runs; the models' answers may.
  • Speed and cost. We did not time the models against the programs, so this report makes no claim about either.
  • Real files. The inputs are our own short cases. Large exports, rare encodings and mixed files were not tested beyond the hostile inputs above.
  • Other models. Only Sonnet and Haiku, both run from Claude Code with tools off.
  • How many ordinary inputs are still refused. We found two gaps by looking. There may be others; a larger set of ordinary text is the next test.

Read this post as Markdown: /blog/data-repair-code-vs-llm.md · Atom feed.