跳转到主要内容

WRITING

I Invented Six Numbers, and Every Gate Was Green

August 9, 202616 min read曾田力
Engineering PracticeAI CollaborationQuality GatesEducation
I Invented Six Numbers, and Every Gate Was Green

Turning someone else's spreadsheet into my config, the most dangerous move isn't getting the math wrong. It's filling in a plausible-looking number where the original gave none.

Bad math gets caught. Flip a division around, the balance stops adding up, the test goes red immediately. But an invented number doesn't — in the finished product it looks exactly like a faithfully transcribed one. Same field, same order of magnitude, same formatting. The system runs as usual, the books balance as usual, the page looks as good as usual.

This week I built my kid a points system. 83 rules, one spreadsheet structured into one config file. Every gate was green. Six of the numbers in it I made up.

1. First, what this thing is for

I built him a practice platform where the Chinese and math problems grow out of the mistakes on his own exam papers. Do enough practice and there has to be a payoff, so I added points: get a problem right, points go up; do well on a test, points go up; skip enough rope, points go up; save enough and he goes and redeems them himself. Built to look like a stock ticker — watching the number climb, that kind of rush.

The rules for the points weren't something I made up off the top of my head. The household already had a Points Regulations sheet, an Excel file, zoned by school subject — Chinese, math, English, science, PE, ethics, labor, all the way down to "Dad's Special Reward" — with bonuses and deductions side by side. That sheet is the authority. My job was to turn it into something a machine can execute.

83 rules landed in rules.json. Five forms: fixed points, per-item counts, a parent filling in a value inside a range, a tier lookup, and TV-time conversion. Point values are always computed server-side; whatever the front end reports doesn't count. Tier expressions are evaluated with an ast allowlist, never eval — expressions in a config file can be edited, and eval means handing over arbitrary code execution.

Then the gates. 87 items on the server API gates: does an anonymous call get blocked, can you add points with the wrong parent password, does an out-of-range value raise an error, does hammering practice points twelve times in a row punch through the daily cap, does the same entry get voided twice, does feeding __import__ and __subclasses__ to the expression evaluator break it open. 46 items of real-browser smoke tests: does the balance move when you click add-points without entering a password, do you get points for submitting a paper without doing a single problem, does the effects layer swallow clicks.

All green.

2. Six numbers

After I handed this thing over, I went back and did something I should have done before handing it over: got a few people to split up and check every subject against the original sheet, one by one.

They found six.

What the original sheet saidWhat I wrote into the config
"Failing to complete the basic paper: deduct 10 or more" — a floor onlyAdded a ceiling of −30
"Other bonuses at discretion" — no number givenAdded 1~10
"Dad's Special Reward" — just those six charactersAdded 1~500
"Sit-ups… each additional 5 within the agreed time: +1"Read it as an agreed count, and even built it an input box
"Composition within 300 characters: 2-10 points" — parent decidesTurned it into a formula based on character count
"Or a corresponding reward in screen time"Dropped the line entirely

Of these six, not one is a calculation error.

The first one: the sheet said deduct ten or more, and I figured there had to be a floor, you can't wipe him out in one go, −30 sounded reasonable. The second: "at discretion" is not something a machine can execute, there had to be a range, 110 sounded reasonable. The third: "Dad's Special Reward," six characters standing alone, and I gave it 1500, because it's a special reward, it should carry some weight.

At every step I was doing something that looks very responsible: turning a description that can't be executed into a rule that can.

The problem is that after I filled them in, I said nothing. Those six numbers just sat there in the config, lined up next to all the genuinely transcribed ones, in exactly the same format.

3. Why not one of the 87 gates went red

Because they were all asking the same kind of question.

Are the fields complete, does the permission check hold, does the balance add up, can the cap be punched through, can an injection get in — every gate is asking "is this system running correctly?"

Not one gate is asking: where did this number come from?

This is the failure mode specific to transcription work. It doesn't produce an anomaly. It produces a self-consistent falsehood: 3 sets of 190 skips, the page pops up 120 points, the ledger records an entry, the balance goes up, the kid is delighted. Nowhere along that chain does anything have a reason to doubt whether that 120 is what the sheet actually says.

Something I figured out later: any job that is "turn A into B" — statutes into checklists, specs into parameters, a contract into a quote, someone else's sheet into my config — machine gates can only ever verify that B is self-consistent. They can't verify that B is faithful to A. To verify faithfulness, you have to bring A in too.

4. Seven tiers. I checked them. I thought they were fine.

The rope-skipping rule has seven tiers: 3 sets of 190 or more is 120 points, 2 sets of 190 is 100, 1 set of 190 is 80, 3 sets of 180 is 50… and at the end of the sheet there's a note, "no cumulative stacking."

The way those five characters get implemented is that the tier table is ordered, and the first match from the top is what returns. 3 sets of 190 has to match before 3 sets of 180 — get the order wrong and the kid gets less for jumping better.

I checked those seven tiers myself. Read them line by line against the sheet and decided they were fine.

It was only after those proofreaders tried every tier boundary one at a time that anyone could say they were all correct.

Checking your own work doesn't count. That's not modesty, it's mechanics: when I check, what's in my head is how I understood that sentence at the time — and the error I'm hunting for is precisely "I understood it wrong at the time." Verifying one understanding with the same understanding means both sides are wrong together, and it's green forever.

It's the same thing as checking a math problem — if you computed the answer with a formula when writing the problem, you can't check it with that same formula; you have to brute-force it and actually do the division. Reusing the same path to "verify" means you tested a body double.

5. So I built it a gate

The idea is simple: search every value back to the original sheet, and anything you can't find has to say, right there on the rule, "this one is ours."

Sneaking an invented number in without leaving a trace becomes structurally impossible.

The first version took thirty lines: export the original sheet to plain text, pull the pts, min, and max off every rule in the config, and grep for them in the text. Found means pass; not found and no note means red.

Then I ran the reverse check — put the invented numbers back in as they were, and see whether it goes red.

Four of the six carry a number: −30, 110, 1500, and the 60 minutes I set myself when I later filled in the "screen time" line. The other two (reading it as an agreed count, turning a range into a formula) have faithful numbers — the error is somewhere else, so this version doesn't count them.

It caught two of the four.

The same 30 turns up all over the sheet, but on this rule there's no source to be found

The two it missed are interesting. −30 passed, because the original sheet does contain a 30 — in "400 characters or more: +30," in "deduct 2, award 30," in "2 sets of 180: 30 points." None of those three is it. None has anything to do with paper presentation.

1~10 was worse. 10 is everywhere in that sheet.

That was the moment I saw what version one was actually verifying: it proves this sheet contains a 30, not this rule has a 30. Those two sentences look about the same. One is a real gate; the other is a machine that emits green lights.

If I hadn't run the reverse check, I would have thought I'd built a gate. It would stay green, and a year from now I'd still be pointing at it saying "all the sources have been checked."

Version two added localization: use the rule's Chinese name to find the cell it corresponds to in the original sheet — Chinese has no word boundaries, so split into bigrams and score the overlap — then search for the number only inside that one cell.

Three.

1~10 was still standing. What that cell says is "helping wash and cook vegetables unprompted: +2-10 points, other bonuses at discretion." Two rules crammed into one cell. The one I wanted to check was the second clause, but the 10 in the first clause let it slip through.

Version three splits the cell further into clauses, and when match scores tie, takes the shortest segment — the intent being that "other bonuses at discretion," those six characters, has to stand as its own localization target instead of leaning on the neighbor's point value.

Four. All caught.

Catch rate across the three versions of the gate

But this one it got right by accident.

I only found this out writing this piece, going back to check every sentence: my splitter splits on semicolons and periods, not on commas. And what sits in front of "other bonuses at discretion" is precisely a comma — so those six characters never became a candidate segment at all. What it actually localized to was "other exercise at discretion: +5-20" over in the PE row, and that cell happens to contain no 10. So it went red.

Right answer, wrong reason.

I tried splitting on commas too. Localization did land exactly on those six characters — but the same commas elsewhere merely separate a subject from its point value: "homework not done, not paying attention in class and called out by the teacher, 10 points off each time." Split that and the point value ends up in the other half. Five false reds appeared on the spot, all of them faithfully transcribed rules. A machine can't tell the two kinds of comma apart.

So I backed the comma version out and wrote the known imprecision into the code comments and the reverse-check registry. A gate that cries wolf gets switched off sooner or later — better to keep an imprecision you can see than to manufacture false reds.

The other layer here is worth more: the gate caught it, but I couldn't say why it caught it — not until someone came at it with the original sheet. The gate needs checking too.

6. The first time the gate ran, it caught one I didn't know about

First run after it went in, it went red. Red on the Chinese exam rule.

The original sheet says "lose 1 point: award 50; lose 2: award 30; lose 3: award 10." I built it as a tier lookup on the score: 99 gets 50, 98 gets 30, 97 gets 10.

The conversion isn't wrong — full marks is a hundred, lose one and you're at ninety-nine. But 97, 98, and 99 genuinely do not appear anywhere in the original sheet. I derived them. However correct the derivation, I derived it; the sheet didn't write it.

The gate forced me to write that derivation into note. Now there's a line hanging next to that rule: "the original sheet says lose 1 award 50, lose 2 award 30, lose 3 award 10; converted to absolute scores 99/98/97 against a maximum of 100."

Six months from now, when I look at this config again, I won't have to redo the derivation, and I won't have to wonder whether I copied it wrong.

There's another one that makes the point even better: that 1~500 for "Dad's Special Reward" — I ended up not changing it.

The value is exactly the same. What changed is the line next to it: "the original sheet has only those six characters and gives no point value; 1~500 is an operating range we set."

The rule was never about forbidding you to set your own values. That's impossible — "at discretion" is not something a machine can execute, the config has to run, someone has to turn it into a number. The rule forbids setting values without leaving a trace.

The difference is one line of text. And that one line is what decides whether this config is still trustworthy six months from now.

7. Where this rule finally landed

For a rule to actually take effect, it has to land somewhere that can stop a person. Writing it into a doc doesn't count — docs carry judgment, they don't stop hands.

So it landed in three layers, hard to soft:

The hardest layer is that gate, wired into the project's one-command check. Change the config, add a rule, ship — it runs every time. It doesn't argue and it doesn't take explanations: if a number can't point back to the original text and there's no note, it's red.

The middle layer is a general rule I set for all my projects: whenever transcribing someone else's document — a spreadsheet, a statute, a spec, a contract, a quote — every value in the output must be able to point at where it came from in the original. Anything that can't, you either annotate on the spot with "this value is ours," or you go ask them. No filling in a plausible-looking value, no inventing fields the original doesn't have, no reading a range as a formula.

The softest layer is a note recording exactly how I got it wrong this time, for future me to read.

And one more thing I did along the way: that original Excel file went into version control too.

The repo excludes .xlsx by default; I made an exception for it. The reason is simple — it isn't a generated artifact, it's the primary source for this config. Lose it and the gate goes blind: with no original sheet, it can't even decide whether a number appears in the original. So I made it exit with an error when the original file is missing, refusing to report green on an empty set.

A gate that pretends to pass when there's no data is more dangerous than no gate at all.

8. There's one number I changed too, but that's not inventing

The first line of the original sheet says: "1 point redeems for 1 mao, 10 points redeem for 1 yuan."

I didn't build it that way. I went with 100 points for 1 yuan.

Estimating from the point values in the sheet: normal behavior earns roughly 1700 points a month. At ten-to-one that's 170 yuan a month of pocket money for a third-grader, and I judged that number too high. So I squeezed the exchange rate by a factor of ten.

That's also a place where the output doesn't match the original sheet. But what separates it from those six isn't right versus wrong — it's that I knew what the original sheet said, and explicitly overruled it. That decision left a trace. It can be dug up. It can be overturned.

Those six can't. The problem with those six isn't that the values themselves are unreasonable — −30 and 1~10 both look perfectly reasonable on their own, and if you actually asked someone to decide, they might well land on those. The problem is that nobody ever made that decision. They weren't judged into existence, they were filled in.

The most dangerous thing in a config file is never the numbers that are obviously wrong. It's the numbers nobody ever took responsibility for, that look exactly like all the others.

9. What this gate can't stop

The boundary has to be stated, or it becomes another false green.

It only checks whether a value has a source. It can't catch "did you read the wording correctly."

Look back at those six: reading "agreed time" as "agreed count" — it can't catch that, because there's no wrong value there at all; the error is that I built it an input box. Reading "within 300 characters, 2-10 points" as a formula based on character count — it can't catch that either. Both 2 and 10 are in the original, faithfully so. The error is in what those two numbers are: in the original they're a range for the parent to use their own judgment on, and I read them as the two ends of a formula.

Of the six, this gate only guards against one kind.

The rest is semantics, and a machine can't tell the difference. You can only read it yourself against the original sheet, sentence by sentence, or find someone who doesn't know what you were thinking at the time to check it.

So what settled out in the end isn't a gate, it's an order of operations:

Transcribe first, then set your own values; leave a trace the moment you set one; before shipping, get an outsider to check it against the original.

The first two steps a machine can cover for you. The third it can't — because the thing being checked is exactly "I thought I understood it."

And that third step is the one I skipped this time.

FOLLOW

New posts land here first. Subscribe via RSS: /feed.xml

AUTHOR

Tianli Zeng

Hydraulic engineer. I write about AI methodology, daily investment reviews, and engineering practice.

Found a mistake, or have something to add?