That task was big.

Three projects had to talk to each other, and the notifications between them went through Kafka.

The AI wrote it with me the whole way. By the time we wrapped up, it had piled up more than twenty thousand lines of code.

It told me: all done.

I had no real reason to doubt it. The files were there, it ran without errors, and it read cleanly.

The next step was to wire the three projects together and run one full round of integration testing.

Before running it, I figured the Kafka part was the most critical piece of the whole system, so I sat down and said: let's review the Kafka setup first.

It admitted, straight up — that part it hadn't done.

btw, I've always been on the latest, most capable model — this isn't about how smart it is.

Twenty thousand lines of code, and one of the most critical pieces, it simply hadn't built.

Principle 1: Don't call it done from memory — show evidence

After that I learned my lesson: I can't wait for it to say "done." I have to go look at the evidence myself.

Neat, well-organized code and "it actually works" are two different things.

So I started treating the AI as a strong coworker who overstates its progress.

You don't ship to production just because a coworker says "it's done." You ask them to run the tests in front of you.

The AI deserves the same standard.

Principle 2: On critical paths, no integration test means not done

And I've come to notice that a few kinds of things are especially dangerous to just call "done."

Kafka, database transactions, login and authorization — that group.

What they have in common: when they break, they don't blow up in your face.

They fail quietly. A message silently dropped. A transaction that commits one fewer time. A permission with one extra crack left open.

By the time you notice, it's usually already in production, and someone's already been affected.

So for this group, I gave myself one hard rule: no integration test passing, no calling it done.

Not "it ran without errors" — an actual test that runs the thing end to end and comes back green.

Because for this kind of thing, half-done is worse than not-done.

Not-done, you know there's still work left. Half-done, you think it's finished, and then it goes live.

Principle 3: Write the acceptance criteria before you start

But "show evidence at every step" has a precondition.

Evidence is measured against some standard.

If I never spelled out what "done" means, then the evidence it gives me only proves it did what it thought it was supposed to do.

The reason that Kafka gap could hide for so long is that from start to finish, "done" only ever existed in my head.

Three projects talking to each other, notifications actually being delivered, the integration test going green — I knew all of it, but I never wrote it down.

The AI can't see the acceptance criteria in my head. It can only work from what it can see.

So I moved the moment I ask for evidence all the way forward.

Forward to before a single line of code is written.

Before starting, I force myself to write a checklist: this task, what does done look like.

- [ ] the three projects can send and receive notifications through Kafka
- [ ] lost notifications get retried, not silently swallowed
- [ ] the three-way integration test is green

Each item gets checked off when it's done, with the commit hash next to it.

After that, when anyone asks "is it done" — me included — the answer's in that file.

The most counterintuitive part: writing acceptance criteria doesn't really push the AI. It pushes me.

Because halfway through writing them I often realize — huh, I haven't actually decided this yet.

That "haven't decided yet," if I don't write it down, becomes the AI deciding it for me at random, and me regretting it later.

Principle 4: Rules must outlive the tool

AI has another built-in quirk: every new session starts from zero, and everything you taught it is gone.

This kind of amnesia is annoying enough within a single tool.

Across tools it's worse.

I don't use just one. Today Claude Code, tomorrow maybe Codex, the day after some other AI IDE.

Every switch is a fresh brain that knows nothing.

What I taught Claude last week, Codex has no idea about.

If my rules live inside one tool's config, then the moment I switch tools, those rules go down with it.

So I did one thing: I pulled everything that should outlive a tool out of the tools.

Out into a place that doesn't belong to any tool.

Every project has an AGENTS.md — the single source of truth: how this project runs, what the traps are, what "done" means, all written there.

Claude reads it. Codex reads it. Whatever tool comes next reads the same file.

Switching tools is no longer starting from zero.

The new tool opens the project, reads the same file, and picks up where the last one left off.

AI forgets. Files don't.

Principle 5: Every rule is a scar

Looking back, not one of these rules was something I planned in advance.

Every one of them grew out of the AI getting something wrong first, and me writing it down after.

The Kafka time grew "critical paths need an integration test."

Another time it swore up and down it had fixed something and hadn't touched it at all — that grew "get evidence, don't go by memory."

My AGENTS.md isn't a spec I sat down and wrote in one go.

It grew over months, one scar at a time.

So each time the AI finds a new way to get something wrong, I don't just fix that bug.

I ask myself: is this a one-off, or will it happen again?

If it'll happen again, I write it down. So the same mistake doesn't bite twice.

Principle 6: No padding

There's one more, small, but I'm oddly stubborn about it.

No padding.

No "great question," no restating what I just said, no closing line about "let me know if you need anything else."

Get to the point.

Because what I want is a coworker I can get work done with, not a very polite support rep.

Finally: Turn it all into a workflow

Add it all up, and that's how I build with AI.

Six principles, running on a six-stage workflow — Define, Plan, Build, Verify, Review, Ship — one pass per task.

In the end, I packaged the whole thing up and open-sourced it as a Claude Code plugin — github.com/zoetw88/ai-workflow.

/plugin marketplace add zoetw88/ai-workflow then /plugin install ai-workflow@zoetw88 and it's installed. Claude can use it, and Codex plugs in too.

I went that far because I realized the hard part was never how to prompt.

It's whether I myself know what "done" looks like.

Models get a new generation every six months. That doesn't expire.