← Back to writing

Self-improving loops

Produce and review isn't enough. What separates a loop that converges from one that doesn't is what you do with what you learned.

aiagentsworkflowengineering

I’ve been using agents to write code for me for a while now. And there’s something that bothers me every time I review a PR: I correct the same things again. Remove the any. Rename variables. Ask for tests on the missing edge case. Kickback after kickback, the same corrections. The agent produces, I review, and the next task starts as if none of that happened.

I’m not sure the agent is the problem. I think the workflow isn’t closing.

The loop we leave half-finished

The typical flow I see goes like this. You give a prompt. The agent produces. You review. If something’s wrong, you send it back. If it’s good, you ship. It looks like a closed loop.

But I think it closes too early. What’s missing is what happens after review: feeding what you learned back into the system.

Full loop
Observe Define the task, give context to the agent.
Produce The agent codes, runs tests, opens a PR.
Review Human review: correct, kickback, approve.
Improve Write the lesson somewhere that persists. Close the loop.
CODING_PREFERENCES.md Rules that feed off real reviews.
Obsidian / Style guides Detailed context the next agent reads before starting.
Without the Improve step, the cycle produces and reviews but never converges. Taste documents are what close the loop.

I think of it as four steps: observe, produce, review, improve. That fourth one is almost never built out. Without it, the system repeats the same mistakes with the same confidence. It doesn’t converge, or at least I haven’t seen it converge on its own yet.

And here’s something that keeps nagging at me: what did the system learn after a human touched the output? If the answer is “nothing,” the workflow is still incomplete. I don’t have a formula to measure that. It’s more of a feeling that sticks around between reviews.

What I built in Bille (and what’s still missing)

In Bille, the project I’ve been working on, I tried to close that gap. I’m not saying it’s solved. I’m still figuring out what works.

The base flow I wrote about elsewhere: tasks in Linear with the “Ready For Claw” label, a coder agent that codes, runs lint, typecheck, and tests, and opens a PR if everything passes. A reviewer that reads the diff, looks for bugs and architecture issues, and kickbacks to the coder if it finds something. Three kickbacks without resolution and it notifies me. That part was already there.

What I added later is different. Every attempt gets written to an Obsidian note: what was done, what failed, what the reviewer decided, how many kickbacks so far. The next agent reads that before starting. Agents are amnesiac by nature. The filesystem isn’t.

And there’s one more step that matters most to me. After I review a PR, a Learning Flow runs: it takes my review comments, analyzes them, and updates CODING_PREFERENCES.md. It’s not a raw log. It’s trying to turn “I removed the any again” into something the next coder reads before writing a line.

Does the number of kickbacks go down? Sometimes. Sometimes not. When it doesn’t, the limit of three forces me to step in. I think that’s fine. I’d rather that than a loop spinning in circles without telling me.

Taste documents

The Obsidian notes are task memory. Taste documents are something else. That’s what I call the style guides that evolve with real work.

In code, CODING_PREFERENCES.md isn’t a copy-paste of generic TypeScript rules. It feeds off my reviews. If I always remove any, that should be written down for the next agent. If I always ask for edge case tests, same thing.

For writing I do the same with this blog. I have a style guide I update after every merge. I compare what the agent wrote with what I actually published. If I always rewrite paragraphs that start with “Furthermore,” that goes into the guide. Next time, hopefully, the agent already knows.

A static style guide gets you started. A living one, I think, is what stops the loop from feeling like pure automation.

The analogy I use with friends: it’s like an intern who gradually learns how you take your coffee, instead of one who gets it wrong every time. The first month they bring you a cortado when you want a lagrima. If you always say “no, lagrima” and that gets recorded, by the second month they should know. Feedback has to go somewhere that persists. Otherwise you’re spending the same effort on every review.

It’s not automatic

What interests me here isn’t just the mechanism. It’s that the improvement step doesn’t come from any tool. I haven’t seen a Cursor feature that updates your preferences after every kickback. There’s no Linear checkbox that says “learn from this comment.” You have to build it on top of the stack.

And it’s easy to skip because the other three parts already work. Produce, review, ship. That wheel turns on its own. The problem is it can turn without going anywhere. Each rotation looks the same as the last if nobody writes the lesson somewhere the next agent will read.

It’s not just code. Any workflow where a human corrects AI output has the same gap. Marketing copy, design, product specs. If the correction doesn’t flow back into the system, the system doesn’t learn. Full stop.

What I still need to figure out

I’m still figuring out what works and what doesn’t. The Bille system is young. The taste documents change with each iteration. Sometimes the Learning Flow suggests something I don’t want in the guide and I edit it by hand. Sometimes I update the guide myself because it was faster.

I don’t have a clear answer on when it makes sense to automate the improvement step and when to do it manually. My imperfect intuition: if you correct the same thing twice in separate reviews, it should be written down somewhere. If it’s a one-off, maybe not.

If you’re using agents and feel like you correct the same things over and over, I wonder if the same thing happens to you. Do you have somewhere corrections flow back into the system? Or is every review an isolated event?

I’m going to keep iterating the flow in Bille. Writing lessons into CODING_PREFERENCES.md, Obsidian notes, blog guides. See if over time I stop removing the same any on PR number twelve. I don’t know if it’ll work. But it feels like the one step I can’t skip if I want this to scale without me becoming the permanent bottleneck.