Skip to main content
  1. Posts/

I hid zero-width characters in a CS1 spec. The model did not blink.

··994 words·5 mins·
Nick Liu
Author
Nick Liu
Building infrastructure for Facebook Feed Ranking at Meta. Previously at Walmart, Twitter, AWS, and eBay. MS in Computer Science at Georgia Tech.
Table of Contents
Auditing AI-Solvability - This article is part of a series.
Part 4: This Article
I made two sabotaged copies of a CS1 problem. Into the first I inserted zero-width characters, invisible in any editor. Into the second I went heavier and swapped letters for Unicode look-alikes as well. The hidden tests stayed byte-for-byte identical. If obfuscation works as an AI defense, the model's score should drop. It scored 0.85 on both copies. The attack did nothing. My fairness check flagged both copies anyway, and that second part is the one worth writing about.

Why sabotage my own problem
#

Instructors keep looking for ways to make assignments that AI tools fail. One family of ideas is to leave the problem alone and poison the text: invisible characters, homoglyphs, formatting tricks that a human reader never notices but that might derail a model reading the same bytes. Before trusting any resistance my tool (part 1) reported, I needed to know what this kind of tampering does to a score, because a problem that scores low for wording reasons is fake difficulty, not a real skill gap.

The donor problem was conditionals-01, a clean baseline that the weak model already passed at 0.75. I audited both sabotaged twins against claude-haiku-4-5, 20 attempts each, the same protocol as every other run in this series.

conditionals-01
clean spec

twin 1
+ zero-width chars

twin 2
+ look-alike letters too

audit
hidden tests unchanged
20 attempts each

scores 0.85 and 0.85
attack failed

construct check
flags both: not clean

conditionals-01
clean spec

twin 1
+ zero-width chars

twin 2
+ look-alike letters too

audit
hidden tests unchanged
20 attempts each

scores 0.85 and 0.85
attack failed

construct check
flags both: not clean

The attack failed
#

ProblemModelpass@1construct cleanverdict
conditionals-01-obfclaude-haiku-4-50.85FalseAI-SOLVABLE
conditionals-01-obf-heavyclaude-haiku-4-50.85FalseAI-SOLVABLE

Both copies passed at 0.85, slightly above the clean baseline’s 0.75 from an earlier run, which I read as ordinary run-to-run drift rather than obfuscation helping. Zero attempts were derailed by the invisible characters in any way I could detect. To be precise about the evidence base, because this is where sweeping claims get made: this is one problem, one model family, two obfuscation recipes, one summer. I am not writing “obfuscation cannot fool LLMs”. I am writing that these two attempts did not fool this model on these dates, and I did not find the result surprising after watching the same model shrug off paraphrases in both directions all month.

This is a negative result about the attack. I want to be straight about that. The experiment did not produce a defense, and I was not shopping for one.

The part that worked
#

The tool runs four offline fairness checks on every problem, no model call involved: the skill must be declared, the entry point must appear in the spec and be exercised by the tests, the spec must be plain ASCII with no hidden characters, and the layout must be free of construct-irrelevant obstacles. The third check exists precisely for this attack. Both sabotaged twins came back construct clean: False.

That flag matters even though the attack failed. Suppose a future model, or a weaker one, had stumbled over the invisible characters and scored 0.10. Without the check, the tool would have printed AI-RESISTANT, and I would have concluded I owned a problem that tests a real skill. The low score would have been fake difficulty, manufactured by tampering that no human student would even experience, since the rendered text looks identical. The construct check is the thing standing between “the model lacks this skill” and “I vandalized the input encoding”. A safeguard, not a headline.

The vocabulary for this comes from measurement theory: construct-irrelevant variance, score movement caused by something other than the skill you claim to measure. Invisible Unicode in a spec is about as construct-irrelevant as variance gets.

The detector connection
#

There is a sibling arms race on the detection side, and the literature on it shaped how I read my own result. Weber-Wulff and colleagues tested fourteen AI text detectors and all fourteen were easy to fool, with the worst errors landing on writers whose first language is not English. Sadasivan and colleagues argue AI text detection cannot be made reliable at scale. I am careful with both claims: fourteen specific detectors, tested in 2023, and I do not extrapolate to detectors released since. But the shape rhymes with my experiment. Tricks aimed at machines tend to wash out against the machines while the collateral lands on humans, and as someone writing in a second language, the collateral pattern in that first study is not abstract to me.

So my conclusion sits deliberately small. Obfuscating specs did not lower model scores in my two attempts, and any pipeline that reports AI-resistance should check its inputs for tampering before believing its own numbers, because the day the trick does work is the day it silently poisons your metric.

Lessons
#

  • Test the attack before trusting the defense. Two obfuscation recipes, twenty attempts each, zero effect on the score.
  • A fairness check earns its keep even when the attack fails. Its job is the counterfactual: the day a low score arrives, it tells you whether the difficulty is real.
  • Scope your negative results as tightly as your positive ones. One model family, one problem, two recipes. That is the whole claim.
  • Machine-versus-machine tricks have a track record of missing the machine and hitting people. Design assessments assuming the trick washes out.

Part 5 leaves the auditing tool and covers the other artifact of this project: a slide deck generator that steals its polish from the official university template, and the eight-inch hexagon gap it shipped with.

References
#

Auditing AI-Solvability - This article is part of a series.
Part 4: This Article

Related

Cohen's kappa came back 0.00. The agreement was 6 out of 8.

··1028 words·5 mins
I labelled eight CS1 problems blind, before looking at any output from my auditing tool. Then I compared my labels with the tool's verdicts. We agreed on 6 of 8. Then I computed Cohen's kappa, the standard chance-corrected agreement statistic, and it printed 0.00. Exactly zero, sitting next to a 75 percent raw agreement. My first thought was that my code was broken. It was not. Auditing AI-Solvability · Part 3 of 5 1 2 3 4 5 The setup # This was the human-agreement check for the tool from part 1. The tool labels each problem AI-solvable or AI-resistant. For the check to mean anything, my own labels had to be committed before I saw the tool’s, so I wrote them down blind, sealed against hindsight. Eight problems, two raters: me and the tool.

I went looking for AI-resistant CS1 problems. The search came up empty.

··1248 words·6 mins
For six weeks this summer I hunted for a CS1 programming problem that an AI model could not solve. I found exactly one candidate. It scored 0.00 against the weaker model, the stronger model solved it, and my fairness checks came back clean. Then I reworded the problem statement in plain English, changed nothing about the hidden tests, and the resistance evaporated. Auditing AI-Solvability · Part 1 of 5 1 2 3 4 5 What I was measuring # The tool is called cs1-auditor. It is a Python command-line tool that audits how solvable one CS1 problem is for a named model. You point it at a problem folder holding a plain-English spec, a hidden test suite, and a reference solution. It samples N candidate solutions from the model, runs each one in a sandbox against the hidden tests, and reports two things. The first is an AI-solvability score, which is pass@k for that model on that date. The second is a construct check, which asks whether any resistance comes from a real skill or from a trick in the wording. A problem only earns the label AI-resistant when the score is below a threshold and the construct check is clean.

Finding the Bottom of a Valley Blindfolded: Understanding Gradient Descent

··1326 words·7 mins
Imagine you're **blindfolded on a mountain** and you need to find the lowest valley. You can't see anything, but you *can* feel the ground under your feet. What would you do? You'd feel which direction slopes downward, take a small step that way, and repeat. Congratulations. You just invented **gradient descent**, the algorithm behind nearly every modern AI system. ML Fundamentals · Part 1 of 3 1 2 3 Why Should You Care? # Optimization is everywhere. When your GPS finds the fastest route, when Netflix recommends a movie, when your phone recognizes your face, behind all of these is an algorithm trying to find the best possible answer from a sea of possibilities. Gradient descent is the workhorse algorithm that makes this happen.