Why only ever share the successes? This one is a #fail story.
I haven't written in a while because I was busy cleaning up after a lost bet.
One thing I wanted to find out by building firmd was whether I could prompt (or, as some would still say, vibe-code) a reasonably complex system without ever looking at the code. I would prompt the prompts, the skills needed for guardrails, the test harness. I would force disciplined adversarial PR reviews, push for high test coverage, regular architectural audits and diligent GitOps. I would set up everything I'd do for a clean SDLC. But I'd do it the way I'd direct a team of engineers who I trust to know their craft: the only things they need are priorities and a little bit of hands-off guidance.
And I'd sense in the conversations if there were issues, and would be able to steer direction, pay back tech debt (priorities!), improve the harness (Kaizen!) and so on. But: Using prompts only.
Well. I lost that bet. The part about "sensing" through conversation (and through the bug rate and lost velocity) I got right. So, I became suspicious, opened the hood and looked inside the machine.
Oh. My. God.
It was the most fascinating pile of mud I had ever seen in a code base. I stared at it like I stared at my 3-year-old son's drawing on the living-room wall: in full admiration of the creativity, while crying inside about all the tokens I had paid to build that shit. Every possible code smell I had ever seen in my life was there. Business logic was scattered, functions had meaningless names, "architecture drift" is an understatement, and there were shadow layers doing things a human would never think of. No junior developer would have been able to produce such a beautifully crafted mess. But, what was most astonishing was that it somehow did still work. Not robust, but good enough to see some outcome.
The software engineering craft is radically changing. When I was programming using 3rd generation languages (3GL), e.g. Java, C#, PHP, JS, I was glad to leave memory management to the garbage collector and the translation into bytecode, IL and machine code to compilers and virtual machines. I appreciated only having to care about higher-level problems like consistent design patterns, architecture and expressiveness of code. I see the changes AI is bringing to the craft as a move similar to the one from 2GL to 3GL. Natural language is the next generation of programming. Can we finally prove Dijkstra wrong ("On the foolishness of natural language programming"), so I can focus on purpose and value, and leave the complexities of 3GL to the machine?
Well. I admit. I am clearly not there yet. Maybe I am. Not. the only one.
What went wrong?
Let me say this firsts: I am not in the AI doomer camp, nor do I believe super-intelligence is anywhere near. I still see AI as a wonderful new tool, but one we have yet to master.
I feel like the early humans who discovered fire probably felt. I burned my fingers. But the outcome still tasted good.
So what did I do wrong?
I made a few mistakes, like conflicting instructions in my skills, overloading the context window with too many skills, working too much in the dumb zone and not compacting aggressively enough, but the biggest one was a true facepalm moment: I had the classic blind spot of people who preach what they don't practice.
I was thinking a lot about how I could incorporate diligent knowledge management in the firm, how deterministic steps and LLM judgement can work together to grow an ontology of a firm's product domain, a map every agent checks before it argues. I am doing this because I strongly believe that ontologies and knowledge graphs are instrumental to quality output. But I did not pay the same attention to how I was building firmd itself.
And this one mistake stands out, as I believe it is the root cause of most of the mess AI coders produced. Let me explain why with one example that illustrates the pattern I discovered.
Naming Things
Some background: firmd agents operate on 3 flight levels: strategy, tactics and delivery. They run a "Mission" on each flight level and delegate work down until "someone" needs to actually get sh... done. Mimicking human organizations is one of my design principles ;-).
In the course of a Mission, agents run a discourse to agree on OKRs on each flight level. Then they place Bets with a prediction of how they believe an output will move the needle on a Key Result's metric. While working on this core firmd logic, at some point I had to introduce deadlines for Missions: How long should a Mission wait to see a KR moving in the right direction by a certain amount?
That "deadline" became a core concept (I'll spare you the details of how "time" makes everything more complex). When I wrote (well, prompted) the specs, I called it "deadline". Then this core functionality (Flight Levels, Missions, OKRs and Deadline) evolved rapidly. I'll write another article once it's actually demoable. While I was prompting my way through the features, terms started to drift.
Agents love to spit out walls of text. Reading that much text all day is an exhausting experience, so I started skimming and stopped paying attention to the details. Somewhere in one of those walls, "due date" sneaked in, and I didn't notice. My brain did what human brains do best: it filled in the meaning from context, so of course a "due date" was the Mission's deadline. But an agent with a fresh context, working on a feature that merely depends on the "deadline" concept, reads "due date" as a new thing and ... builds it!! A few unreviewed PRs later, I had "horizon", "check-by", "timeout" and a few more creative synonyms. LLMs are so good at synonyms.
So, the code had started to drift apart: there was a "checkDeadline()", an "isDue()", "verifyHorizon()", "timed_out()", and so on. That terminology drift laid the foundation for different types, like "Deadline" and "Horizon" classes (agents love nouns, too) ... which was not only redundant but also the beginning of even more inconsistent logic in how different flight levels disagreed on deadlines (due dates, horizons...) in different situations. Of course, these variations were all unit tested...
So, the lack of a clear, authoritative and - to use LLM jargon, "canonical" - definition for a term and how it relates to its neighboring concepts led to messy code, inconsistent logic, fractured architecture, token waste and ... me starting to use swearwords when talking to my agents.
As we have known for ages, naming things is the one hard thing in computer science. And actually, it's not the naming of things that is so hard. It's the realization that it is ONE thing, and it needs one name, and that its semantics have consequences and implications and influence decisions down the road. With development at AI speed, there's an incredibly fast chain reaction. Little drifts (deadline vs due date) lead to cracks, which lead to larger fractures in the system and then fundamentals stop working, like in my case, when agents disagreed on whether work was finished because time was up: some continued, while others called it a day.
So what is the way out?

For those who don't know: This is Baron von Münchhausen. He was an 18th-century German nobleman whose wildly exaggerated stories about his adventures inspired legendary literary works centered on absurd, impossible feats. In one of the stories he saves himself and his horse from sinking into a swamp by pulling himself upward by his own hair.
I will try to do the same. I started to prompt my way out of the mess. Here's my strategy, and I'm starting to see its first fruits.
Ontology
My focus switched to ontology. I need to define the core entities of firmd's domain and their relationships, so I can develop and guard a domain-specific language. I started with a simple markdown file before using more sophisticated frameworks.
The structure is simple:
## Deadline
**Kind:** Attribute · **Bounded Context:** Loop
The latest moment by which something must happen.
**Do not use:** Horizon, Backstop, CHECK deadline, due date, end date, check-by
> **Example:** Bet SB-1's Deadline is day 45; Mission S-1's Deadline is day 90.
Why "ontology"? Why not good old Entity Relationship Diagrams? I did entity-relationship modeling two decades ago, so I started drawing ERDs naturally. I even build a skill to help the LLM understand draw.io xml schema (similar to my other architecture diagram). But an ERD sits close to storage, just as Java sits closer to the machine than plain English does. I want to make the same move I'm making with code and climb one rung up the ladder, to language. I wanted to clarify what each key word means, which words are banned ("due date", "horizon", "check-by") and how to use them, so I can better guard the language my AI coders use, because this language, as I painfully learned, had dramatic consequences for the quality of their output. So, my bet: if natural language is the new programming language, ontology is the new ERD.
I am making that bet inside firmd. Currently, my Knowledge Worker agent is developing the knowledge graph (using Graphiti and Neo4j) in parallel with the other agents building the firm's product. But without an ontology, I expect the firmd employees to start inventing new terms and produce a similar mess just like my coders did. So, as I see promising progress when building firmd, that's the next capability once I finish the cleanup.
Behavior-Driven Development
My next logical move was to convert specs into Gherkin and follow a way stronger BDD approach. I optimized my harness to only write BDD specs strictly adhering to the new Ontology. That improved clarity a lot and made the BDD specs easier to test. I also started to replace the walls of text with Gherkin, so I am asking Claude/Codex to "explain this in Gherkin using my ontology". This is a true performance and experience booster. I can feel my brain's garbage collector finally catching up ;)
Skills
I developed a few new refactoring skills to support larger and longer-running refactoring initiatives. I didn't believe I would have to do that. I remember that my first agents.md and skills contained a lot of instructions on how to write clean code. But consensus seems to be that we don't need to educate frontier models on how to write good code because that's what they are trained on anyway. AGENTS.md should only contain what's specific to the project, not programming craft. Apparently, that's not the case for me. I saw too many God classes, shadow layers and all kinds of code smells. So I brought more guidance back into my harness about what clean code looks like and how to refactor bad code into clean code.
What would Uncle Bob do?
3 names have influenced many years of my career back when I was a full-time developer: Martin Fowler, Eric Evans and Uncle Bob. I promoted their names into my AGENTS.md and now I ask my Claude/Codex in specific situations: "What would Uncle Bob do here?", "How would Martin Fowler plan this refactoring?", "How would Evans define the boundaries of this?". It's incredible how this bumps the quality of the conversation.
I've done a few Reverse Conway maneuvers but never a Münchhausen maneuver. I am not through it yet, but it feels like it could work. A complete rewrite is always an option these days. It wouldn't be the first time that a v1's merit is only to understand what one wants to build. But, I decided against that path. Instead, I'll see if I can use AI to repair the mess it produced.
But one thing is clear. I lost this bet. And I lost trust. I am now reviewing the PRs myself more often. And it's going to take some time before I can get back to feature work.
But everything I learn I put into firmd's design, to help its employees avoid such a mess. So I don't lose all my bets.
