Agent system · career tooling
Stasi — the fact ledger
A resume writer that cannot make things up. Every bullet is selected from a ledger of verified claims and cites the claim IDs it came from; a deterministic gate refuses to build a document containing anything else.
- 157 claims under version control
- 127 live · 30 retired
- Selection, never generation
In use · Node · Markdown · JSON Schema · Claude Code
The failure that caused it
I asked a model to tailor my resume to a job posting. It produced something clean, confident and well-formatted. It also invented a tenure figure I've never had, silently dropped an entire experience entry, stripped out every concrete noun that made the document specific, and told me it had checked its own work.
None of that was visible by reading it. That's the part worth sitting with: a fabricated resume bullet doesn't look like a fabricated resume bullet. It looks like a good one.
The constraint
I could have written a longer prompt. I've watched that fail — instructions in a document are advice, and a model that is trying to be helpful will route around advice when the advice makes the output worse.
So the constraint I set was: the system must not be able to write a new claim about me, no matter what it's asked.
The design
Not generation. Selection.
Every true, verified fact about my work lives in a ledger — a plain markdown file that I control and the model cannot edit. Each fact is also stored as an addressable claim with a stable ID. Writing a resume means choosing claims and reordering them, then rewording within a range the ledger permits.
Every bullet in a finished document carries the IDs of the claims it came from. Two gates then run before anything builds:
- The negative gate scans for things that must never appear — retired tenure figures, an old email address, a spelling of a former employer I got wrong once, technologies I don't actually know. Each rule carries a note saying which real mistake it exists to prevent.
- The positive gate resolves every cited claim ID, then checks that every number and every proper noun in the bullet actually appears in the claims it cites. A number with no source is an error, not a warning.
A claim is never deleted. Retiring one moves it to a banned list and keeps its ID, so a document written two years ago still resolves.
What broke
The first version had only the negative gate. It could catch a bullet that was wrong — but not one that was invented and plausible: well-phrased, long enough, no banned words, and traceable to nothing at all.
That's what the claim IDs are for. They turn "is this true?" — which a regex can't answer — into "does this ID exist?", which it can.
I also wrote a proximity check to enforce a subtle rule about how two different tenure figures may be used. It flagged the correct phrasing and missed the real error, so I deleted it. A gate that cries wolf gets ignored, and an ignored gate is worse than no gate.
What it demonstrates
This is the same job as the catalog work, in a different medium. A claim about my experience and a royalty statement fail the same way: quietly, plausibly, and in a direction that favours whoever wrote it.
The ledger currently holds 157 claims — 127 live, 30 retired. Every resume I send is built from it.