← All writing
Daily Driver 2 / 7

74 tests failed. Nothing was wrong with the app.

One night my test suite reported that most of the product was broken. Nothing was actually broken. The next step in that queue is an agent whose job is to make failing tests pass, and the only reason it stayed asleep was a number I had set for an unrelated reason.

Aug 5, 2026 7 min read Original

I was in the coffee shop by half past eight and the overnight summary was already on my phone. 194 passed, 74 failed.

Then the list. Onboarding. Sign-up. Services. Settings. The trial offer. 24 files, most of the product, all red.

The report was telling me something major was going on. Nothing was actually wrong with the app.

A Postgres update had installed itself on that machine during the night and changed the way it hands out permissions. Every test that touches the database failed, and almost every test touches the database. The app was fine. The floor it was standing on wasn't.

Not one of those 74 tests could tell me that. Each only sees what happens in front of it, a button that never appeared, a page that never loaded. The layer underneath knew exactly what was wrong and said so 418 times that night, into a file nobody was reading.

26 July · 194 passed, 74 failed, 51 minutes
What the report listed 24 test files
onboarding 7services 7settings 3gating 2first AI use 2calendar 1recipes 1telemetry 1
What was actually wrong The database refused every connection.
24 files, one cause. Read the list and half the product had fallen over. Nothing in the list was broken.

The part that actually bothered me

51 minutes of machine time went into a run that could never have passed. That's the cheap part. I was asleep.

Here's the expensive part. When that suite goes red, the next thing in the queue is an agent whose whole job is to make failing tests pass. It's quite good at this.

Hand a person that pile of failures and the reply comes back with a hedge in it. Everything is failing at once, which seems unlikely, so it might be something on my end. Before you tell the room the building is on fire, you check whether it's your own smoke alarm. An agent doesn't send that message. It says yes and starts work.

It didn't start work that night, and the only reason is a number. There's a cap: more than five failing specs and it stays out, because five failures at once are usually one cause, and patching them one at a time papers over something bigger.

74 is more than five.

So what saved me was the size of the breakage. I set that cap to catch systemic regressions in the code. It was never meant to protect me from a broken machine, and it only managed to because this machine broke completely.

Imagine the update had taken out a corner of it instead of all of it. Three or four tests fail. The cap waves it straight through, and an agent spends the next four hours working on a database that is refusing every connection it receives.

A stupid gate in front of a very capable worker

Two things changed the next morning. The first is almost embarrassing: before anything runs, something now checks whether it can run at all.

The catch is where you ask from. That morning I checked the database by hand and it answered me perfectly. Of course it did. I wasn't standing where the tests were standing, and they don't reach it the way I do. The check now asks the same question the way they have to.

The second change matters more, and it has nothing much to do with tests. The night now has a third possible outcome: "I couldn't run." Before there were two, passed and failed, and if you only give something two answers it will hand you one of them even when neither is true.

Deciding which of the three it is takes no intelligence at all, and I want to keep it that way. The thing waiting behind that gate is clever, tireless and willing to help. None of those are qualities you want in whatever decides to wake it.

What the agent is actually for

None of this is why the AI is there. Running tests on a schedule is a solved problem and has been for 20 years. A timer can do it. A timer has never once told me what's wrong.

Here's a night where the suite really did fail. The verdict lands six minutes past two. The agent starts nine minutes after that. By half past there's a pull request open with a per-failure explanation in it, and I'm asleep for all of it.

One red night · 01:30 to 02:33
01:3002:0602:1502:33
checkrun the suitejudgereproduce, fix, write upPR waiting
Everything grey is a script. The one block in colour is the only place a model is involved, and it is the only part of the night I could not have written myself.

What it does in those 18 minutes is the work I never had time for. It reproduces the failure. It decides, for each one, whether the app is broken or the test is broken, which is the judgement call that used to eat my morning and the one thing no amount of scripting ever did for me. Then it fixes whichever it was, runs it again, and writes down what it concluded.

Both times it ran for real it came back with the same answer: this is the test's fault, not the app's. And both times it added something I didn't expect. It couldn't fully verify the fix, so it said so, on the first line of the pull request, in capitals. It's required to. A fix it couldn't run isn't a fix, and it isn't allowed to let me read it as one.

So the machine can hedge after all. It just has to be made to, which is most of part four of this series.

And it stops there. It never merges. That line hasn't moved since part one.

The loop keeps what it learns

This is the thing I was trying to describe in part one and couldn't do properly without an example.

If I did this once a quarter, that night would have cost me a morning and taught me nothing. I'd have fixed the permissions, sworn a bit and moved on, and the next time a machine lied to me I'd have started from zero again.

Instead the night is still in there. It changed three things, and all three are still running tonight.

What one bad night left behind
RunJudgeActLearn
and back into the next run
A check that runs first asked the way the tests have to ask itA third answer "I couldn't run" is now a thing it can sayThat night's own words the exact text the database was printing, kept
None of these existed on the 25th of July. One night produced all three, and they run every night now.

The third one is my favourite. The output that thing watches out for is the literal text the database was printing at two in the morning, copied straight out of the log, so that particular lie can never work twice.

Which is the difference between a job you automate and a loop that improves. One does the same thing every night. The other gets slightly harder to fool each time somebody fools it.

Here's every night since, including the ones I'm not proud of.

Every night since · 26 July to 5 August
74 26.7 1 27.7 0 28.7 0 29.7 10 30.7 9 31.7 34 1.8 34 2.8 34 3.8 35 4.8 0 5.8
nothing was broken something was clean night
Taller means more tests failed. The hollow one is the night nothing was actually broken. Every other bar is real work to do.

Setting this up was quite complicated and full of false alarms. I still have to turn a few screws from time to time. But the green nights are boring and the red nights are worth reading, which is what I wanted and didn't have.

Anything you hand over needs a way to say "it wasn't me"

If you're pointing an agent at anything, this is the bit I'd steal.

Work out what happens on the day it can't do the job at all, and make sure that looks different from the day the job goes badly. Those are different sentences and people say them differently without being taught. A new colleague says "I couldn't get in, is my access set up yet?" A system says FAILED and waits for you to work it out.

It matters more with an agent than it does with a person, for one reason.

An agent never refuses the assignment.

Someone who thinks the question is wrong pushes back before they start. You've had that for free your whole career, from every colleague you've ever briefed badly, and it's the first thing you stop getting when you hand the work to a machine.

It's the first of several things I've had to teach mine to doubt. The most uncomfortable one is part six, where a marketing agent grades her own predictions every week and publishes a hit rate she doesn't come out of well.

The series Daily Driver Seven parts on running a one-person software company where agents do most of the execution, and where the line still sits.
1 / 7 AI did not give me my time back read
2 / 7 74 tests failed. Nothing was wrong with the app. you are here
3 / 7 The updates I never had time to check read4 / 7 I still answer every support mail myself read
5 / 7 My AI CMO publishes her own miss rate soon
6 / 7 I had five people and we still never did this soon
7 / 7 My AI team costs €451 a month soon

If you're a founder working out how much of this you can hand over, and where the line should sit, that's the work I do with teams. Let's talk.

Stefan Malzner
Stefan Malzner

Product designer in Vienna · founder of Franz. I write about product, design, and building software that lasts.

Get in touch

Let's talk.

Whether you're shipping a product or planning an event, the fastest way to reach me is email. I read everything that lands.