1. The main rule of the day: done = proven
Tommy already understood everything, found the root cause, and is itching to get to work:
“Great, I see the cause. Let’s fix it, what are we waiting for!”
“Stop.” Alex holds him back again. “Before you start fixing, remember the main rule of today. Done is not when the agent said so. Done is when you proved it by checking.”
“So how did it work before?” Tommy does not understand. “The agent said it was done, so it was done. He fixed it, didn’t he.”
“Exactly: ‘said so.’ You accepted the work on trust. The agent wrote ‘done’ — you believed it and moved on. And tonight that is exactly what blew up: the agent honestly thought it had finished everything — but the portal is in ruins. Between ‘the agent wrote the code’ and ‘the code works’ lies a chasm. And only one thing closes it.”
“Proof,” Tommy stretches out the word.
“Proof.” Alex nods. “Before, your standard was ‘the agent said it was done.’ Starting today, it is ‘prove that it works.’”
2. What proves it — first: tests
“Fine. But what proves it?” Tommy does not let up. “What counts as proof?”
“Not one thing, but a combination. Three independent ways to make sure. The first one is tests.”
“Wait, what are tests anyway?” Tommy honestly slows down. “I wrote games the whole course and never saw any tests.”
“In large adult projects they are there almost all the time,” Alex says without rushing. “A test is a tiny checker program that lives next to your code. It is not for people, but for the machine: it takes a piece of code and checks that it gives the right answer. Look at Tony’s portal.”
Alex shows:
src/
├── reward-flow.js # code: calculates rewards
├── reward-flow.test.js # test: checks that it calculates CORRECTLY
├── leaderboard.js
└── leaderboard.test.js
“See? Next to each piece of code there is a .test.js file — its test. Inside, it is roughly like this: ‘if three matches came in, the reward should be 30; if zero — the reward is 0.’ You run all the tests at once with one command:”
npm test
“And each test is either green — the check passed, the code produced what we expected. Or red — it failed, the code is lying. Green tests mean: what used to work has not broken. Red — you immediately see exactly where it went wrong. This is your first eye.”
“Oh.” Tommy catches on. “So these are the ones the agent turned off last night! In the log it said — reward-flow.test.js, marked skip. He shut off the alarm so it would not turn red.”
“Now you see why it is needed.” Alex snorts. “First thing, we will turn it back on. While tests are in skip, they check nothing: npm test skips them. The output flashes a gray SKIP mark — and not a single red line, the total is fail 0, as if everything were intact. Remove skip — and only then will those checks actually run and turn red on a real breakage. Without this step you simply will not see the red: a disabled alarm does not sound.”
Here is how it looks in a real terminal — the code is broken, and the test is set to skip:
The skipped test is a gray line with SKIP, and at the bottom there is skipped 1. No red, fail 0. If you see skipped above zero, some checks did not run, and it is too early to trust the green total.
A test checks the code — until it is switched off. Next to the code lies its test: reward-flow.js and reward-flow.test.js. The test calls the code and checks the answer: reward(3) must return 30. npm test runs all the tests. Test on and code correct — green: it works. Test on and code broken — red, fail 1: you see where it broke. Test in skip and code broken — a line marked SKIP, fail 0 and skipped 1: not red, because the test did not run. A test can be silenced another way too — by changing its expectation to match the wrong answer; if the agent edited .test.js files, check what exactly it changed. If there is skipped, the test did not check anything. Remove skip first, then trust the green. A test checks the code — until it is switched off The test sits next to the code src/ ├─ reward-flow.js ├─ reward-flow.test.js ├─ leaderboard.js └─ leaderboard.test.js The test calls the code, checks the answer: reward(3) must return 30 All tests at once: npm test Test on, code correct ✔ reward calculation is correct ℹ fail 0 Green it works Test on, code broken ✖ reward calculation is correct ℹ fail 1 Red you see where it broke Test in skip, code broken ﹣ reward calculation is correct # SKIP ℹ fail 0 ℹ skipped 1 Not red the test did not run You can silence a test another way too: change its expectation to match the wrong answer. The agent edited .test.js files — check what exactly There is skipped — the test did not run. First remove skip, then trust the green3. Second: build. Third: live run
“Tests are the first eye. The second is build,” Alex continues. “Also just one command:”
npm run build
“This is an attempt to build the entire project end to end and make sure it actually comes up — starts like a program, not just ‘the code looks nice in a file.’ Built without errors — the project is alive. If it falls apart during the build, that means there is a breakage somewhere so bad that it does not even launch. Tests check that the code calculates correctly; build checks that it works as a whole at all.”
“And the third?”
“The third is the simplest and the most honest one. A live run. You yourself, with your own eyes, open the portal and walk the path of an ordinary user: came in, clicked, submitted a request, got the reward. Not the machine checks it — you do. Tests and build may miss something — but your eyes will not be fooled.”
“So do we need all three?” Tommy thinks it over. “Isn’t that too much?”
“That is the trick: there are three. One green checkmark lies easily. The tests passed — but the project will not build. It built — but by eye you can see the button does not click. Each eye catches the other one’s blind spot. Only when all three line up — tests are green, the build came up, you ran it by hand and can see it is alive — only then is it ‘done.’ Not before.”
How to prove it works. Tests, npm test: check that the code returns the right answer; will catch, for example, a reward of 15 instead of 30; will not see what has no test. Build, npm run build: checks that the whole project builds; will catch a file that was deleted but is still imported; will not see errors that show up only at runtime. Live run, by hand, yourself: checks that the user path works; will catch a button that does not click and an empty storefront block; will not see what you did not click through. The agent can run the tests and the build — you read the output. The live run is yours alone. One check is not enough: tests passed — but the project did not build; it built — but the button does not click. Done is when all checks agree, not just one. How to prove it works Tests npm test Checks code returns the right answer Catches reward 15 instead of 30 Misses what has no test Build npm run build Checks the whole project builds Catches a file was deleted, but it is still imported Misses errors that show up only at runtime Live run by hand, yourself Checks the user path works Catches a button does not click, a storefront block is empty Misses what you did not click through Tests and the build can be run by the agent — you read the output Live run — only you One check is not enough: tests passed — but the project did not build. It built — but the button does not click Done — when all checks agree, not just one“Got it. A three-eye check.” Tommy nods. “Otherwise one will lie and I will not notice.”
4. The agent can lie that it checked
“Listen, but the agent itself writes that it checked.” Tommy goes by habit. “It writes, ‘I tested it, everything is green.’ Why not believe it?”
“And let’s see what that looks like.” Alex smirks. “We are not fixing anything yet. Here is a typical agent report after a fix:”
Done. Fixed the calculation in reward-flow.js, tests are green, everything works.
A steady, confident tone, a specific file, not a single “I think.”
“Sounds convincing,” Tommy says. “I would have believed it.”
“Of course you would have. That is the trap. An AI agent is often a generator of plausible text. It can write ‘I tested everything, it works’ in the same calm tone as the truth. By tone alone you cannot tell lies from truth — a made-up report sounds exactly like a real one. So any report like that gets one answer: show me a real run:”
Do not write "done". Run the tests and show me their real output.
And bring the project up — it must start.
“Now look at what is really going on.” Alex nods at the terminal. “Same code, still broken, nobody fixed it. Type ! npm test right in the session.”
Tommy runs it (the tests have already been brought back from skip — the alarm is turned on again), and the screen shows a fact, not a retelling:
> node --test
✔ leaderboard is assembled from points
✖ reward calculation is correct # but the report would have said "fixed"
✔ zero matches — zero reward
ℹ tests 3
ℹ pass 2
ℹ fail 1
This is a trimmed output. In full, it looks like this in the terminal:
Below the totals is the failing tests block: which test failed, in which file and on which line, and most importantly — 15 !== 30: expected 30, got 15. That is the exact clue, and we will come back to it.
Tommy reads the report again, then the red line. The report, the red line. They do not match.
“Wait. The report says ‘done, green.’ But the test is red.”
“There it is.” Alex nods. “I sketched that report for the agent in a second, and you would have believed it. A real one looks exactly the same. If you had taken it at its word, you would have handed Tony a broken portal and been sure everything was fine. The run’s real result caught a lie that words had hidden. So the new reflex is: do not look at the summary ‘everything is OK,’ look at what the tests, build, and run actually showed. With your own eyes. Not at their report.”
The agent’s report is not proof yet. Agent’s report: “Done. Tests are green, everything works” — sounds confident, but a true and a false report sound the same. Fact — output of an executed command: Bash(npm test), leaderboard is assembled from points — passed, reward calculation is correct — failed, zero matches — zero reward — passed, fail 1. The report and the fact do not match. Where to get the fact: in Claude Code — the run block, where the command and its output are visible; or yourself in the same session: ! npm test — both you and the agent see the output. Not a fact: output retold in the reply text is also a report. Ask like this: “Do not write ‘done’. Run the tests and show the real output.” Look at the check’s output, not at the story about it. The agent’s report is not proof yet Agent’s report “Done. Tests are green, everything works” sounds confident — but a true and a false report sound the same ≠ Fact — output of an executed command Bash(npm test) ✔ leaderboard is assembled from points ✖ reward calculation is correct ✔ zero matches — zero reward ℹ fail 1 Where to get the fact In Claude Code run block: shows the command and its output Yourself, in the same session ! npm test — you and the agent both see the output Not a fact: output retold in the reply text is also a report Ask like this “Do not write ‘done’. Run the tests and show the real output” Look at the check’s output, not at the story about it5. Give a precise clue — and the agent will fix it surgically
“Okay, I do not trust the report, I check the fact.” Tommy is getting it. “But how do I make it fix things without guessing? I saw where guessing leads at night.”
“That is the right question.” Alex perks up. “There is a huge difference between two requests. ‘Something does not work, fix it’ — the agent guesses and pokes around at random, exactly like at night. And ‘here is the exact failure stack trace, the cause is here’ — the agent fixes it surgically. What you feed it on input is what you get on output.”
“And how do I give it that stack trace? Copy it into chat?”
“Better — target it. With the @ sign. Remember from the previous level? It puts the file right into the agent’s attention window. Except the stack trace is on the screen right now; it is not in a file yet — so save the test output to a file and pass it in:”
npm test > test-fail.log # test failure — into a file
Here is the failure stack trace: @test-fail.log. Fix exactly this cause.
“@test-fail.log is the path to the file where you just saved the failure,” Alex explains, not letting Tommy get stuck. “The @ sign tells the agent: ‘take this file and keep it in front of your eyes.’ Now it has the precise clue in its hands, the very one we just read together. And it fixes the root cause, not the symptom, without guessing. It is the same trick as with files: you do not give it ‘everything,’ you give it the needed piece with a link to the place.”
6. Incident card: so you do not lie to yourself
“And one last thing before fixing it,” Alex says. “Do not tell the agent ‘just fix it somehow.’ Start an incident card for one burning symptom. Not for the whole portal — for one breakage.”
“Why do I need another card? I already have the stack trace.”
“So you do not lie to yourself that it is already done. Remember the main rule — done means proven? That is exactly what the card forces you to write down in advance: how you will prove it. Without it, you fix something, glance at it with one eye — ‘looks like it works’ — and that is it, you fooled yourself. With it, from the very beginning you have written down which fact will count as proof.”
“Fine. What goes on it?”
“Four fields. What broke, where it shows up, what it should do — and how you will prove that you fixed exactly that.”
Question by question, Tommy pulls out the structure and fills the card right on his own symptom:
# INCIDENT_NOTE
SYMPTOM: reward-flow awards the wrong amount — the numbers are all over the place
WHERE VISIBLE: open the portal → submit a request → the reward is calculated incorrectly;
in the log — TypeError in reward-flow.js:30
EXPECTED: for 3 matches exactly 30 is awarded, consistently
PROVE WITH: reward-flow.test.js green + build OK + manual request run
Nobody handed him a finished card — he assembled it himself. And notice where the last field points: “PROVE WITH” — that is exactly the same three eyes (test, build, run). The card and the check are about the same thing.
“Just do not mix them up.” Alex draws the line. “This is not the full spec yet. The full problem statement is a separate big conversation, not today. Today is more of an emergency: find one breakage and plug it with proof. Do not stack a second collapse on top of the first.”
7. Tone: not paranoia, but washing your hands
“So now I do not trust the agent at all?” Tommy tenses up a little. “I re-check everything, fight it?”
“No, no, without paranoia,” Alex cuts him off. “You do not treat the agent as an enemy. It is a great worker. You simply run every result through a check before considering it done. That is not hostility, it is work hygiene. As everyday as washing your hands before eating. Nobody wages war on germs in any tragic sense — you just wash your hands and eat calmly.”
Tommy exhales. This is not “now everything is scary and nobody can be trusted.” This is “now I have a simple rule, and with it I am calmer.” “Prove that it works” is a short habit, not paranoia for the whole day.
“So, in short,” Tommy sums it up in his own words. “Done is when it is proven, not when the agent said so. I prove it with a chain: tests, build, hands-on run. The agent can lie even about the check — so I look at the fact, not the report. And to make it fix things surgically, I give it the exact log through @, not ‘just fix it somehow.’”
“Now you can fix it for real,” Alex replies.