← Writing

I built a résumé generator. Then I caught it lying.

I'm job hunting right now, so I did what engineers do instead of the actual scary thing: I built a tool.

It pulls live postings from 164 companies' job boards, scores them against a weighted profile, and generates a résumé tailored to whichever one I pick. There's a section called Key Qualifications for This Role, and it gets built by intersecting two lists: skills that appear in the job posting, and skills I actually have.

That intersection is the entire honesty guarantee. It's the thing that's supposed to make this ATS optimization rather than lying.

This week a recruiter sent me a Senior AI Engineer role — a startup I'd genuinely want to work at. I ran the generator. Out came a clean résumé with eleven key qualifications, and the eleventh one was:

Fine-tuning

I don't know how to fine-tune a model.

Where the lie came from

The matching logic is about six lines. There's a dictionary that maps a skill token to how it should be phrased on my résumé:

KNOWN = {
  "python":   "Python",
  "rag":      "RAG",
  "fine-tun": "Fine-tuning",     # <- the problem
  ...
}

The generator walks that map and keeps every entry whose token appears in the posting.

Read that sentence again, because the bug is inside it: it keeps every entry whose token appears in the posting.

The posting is the wrong place to look. A token showing up in a job description is evidence about what the employer wants. It is not evidence about me. The only thing standing between "skills mentioned in the posting" and "skills I have" was the fact that I'd hand-written that map — and months earlier, I had typed "fine-tun" into it because it felt like something an AI engineer ought to be able to say.

The map was never a record of my skills. It was a wishlist that had quietly been promoted to a source of truth.

I've seen this bug before

I spend my other hours building systematic trading strategies, where the deadliest class of bug is the one where information about the thing you're predicting leaks into the thing doing the predicting. Your model looks brilliant because you accidentally showed it the answer.

Same shape here. My résumé generator was optimizing to match the posting, using a signal derived from the posting. The output looked like a strong fit because the fit had been constructed out of the target.

I have a whole discipline for this on the trading side. Pre-register the hypothesis before you test it. Model the costs honestly. Throw out what doesn't survive. I had a strategy last year showing a profit factor of 3.15; I modeled realistic fills and it collapsed to 1.11, so I killed it. That's the job. I've killed far more ideas than I've kept.

It had not once occurred to me to point that same suspicion at the tool I was using to describe myself.

The fix, and the part I didn't expect

The fix was deleting one line. I left a comment in the hole where it had been, so that future me doesn't helpfully put it back:

# NOTE: no "fine-tun" entry on purpose. Postings mention SFT/DPO/GRPO
# constantly and I have not done hands-on fine-tuning - auto-claiming it
# is the fastest way to fail a technical screen.

Then I went through the rest of the map and asked a question I hadn't asked when I wrote it: can I defend this in a screen?

"AWS" came out. I run a Linux VPS and a managed Postgres. That isn't AWS.

But here's the part I didn't see coming. That same audit — going entry by entry asking "do you actually have this?" — ran in both directions. And it turned out I was under-claiming far more than I was over-claiming.

Asking the question honestly surfaced four things that were missing from every résumé I had ever sent:

Four real credentials, none of them on any résumé I'd sent out. One fabricated one, sitting near the top of the page in a section literally titled Key Qualifications.

The audit deleted one line and recovered four.

If you're building one of these

Every ATS optimization tool I've seen is built to maximize overlap. That's the whole pitch: feed it the posting, get more match. But a system that can only ever add isn't a filter. It's an amplifier, and what it amplifies is the distance between who you are and who the posting wants.

The useful version has to be able to subtract. It needs a representation of you that exists independently of the job description, and it has to be willing to hand back a shorter list.

Mine now returns a smaller Key Qualifications section on about half the postings I run it against. That isn't a regression. That's the feature.

That Senior AI Engineer role still lists hands-on fine-tuning as a requirement. I'm applying anyway — the recruiter came to me, and I would much rather say "I haven't done that, here's the adjacent thing I have done, and it's exactly why I want this role" than get found out in month two.

I'm just saying it in my own words now, instead of letting a dictionary say it for me.