The last post ended on that late night: the report was all green, I opened the browser, and the game was dead. Students couldn't get in, the start button did nothing, and the questions the teacher picked never made it to the server.
My first reaction was every engineer's reaction. These are a few bugs, fix them one at a time, move on. But something kept nagging at me. How does an all-green pipeline produce a thing nobody can play? If this round was just bad luck, what about the next one? Rather than patch them one by one, I wanted to know the real question underneath: what is it, exactly, that lets an all-green report and a dead build be true at the same time?
So I decided not to put my head down and fix it. I handed the whole package to Fable 5 — the code, the all-green report, the spec — and said one thing: tell me what's broken. I deliberately gave it none of my guesses. I didn't even say "I think the selected questions aren't reaching the server," because I didn't want it to nod along with my diagnosis. I wanted its own eyes. That decision — not framing the reviewer — turned out to be the most valuable move of the whole thing.
Three symptoms, one blind spot
Fable's first finding stopped me cold. Those three symptoms weren't three bugs. They were one blind spot, falling on three different edges.
My whole pipeline, end to end, thought of the product as a collection of modules. The room is a module, the question bank is a module, the student interface is a module — each one defined, owned, and verified. But what about between the modules? How pages navigate to each other, what a button triggers when you press it, where data flows from and to. Those edges were managed nowhere: not in the spec Q&A, not in the task breakdown, not in the acceptance checklist.
Students couldn't get in because the edge "where should the home page link to" was owned by nobody. The button was dead because the edge "what should pressing it trigger" was owned by nobody. The selected questions never arrived because the edge "how does the teacher's choice flow to the server" was owned by nobody. Three symptoms, one disease: my world had points and no lines.
This landed exactly on the trap I'd set for myself in the last post. Back then I was still congratulating myself on the loose coupling between my tools, never noticing that I'd quietly turned every seam in the system into no-man's-land.
The finding I least wanted to admit: my quality check was manufacturing the defect
But the one that really got me was what came next.
My task breakdown tool had a self-check rule I was rather proud of when I wrote it. Every acceptance criterion must be claimed by exactly one task, and must be verifiable independently and mechanically. Sounds rigorous, doesn't it. Nothing missed, nothing doubled, everything checkable.
The problem: "teacher presses start, students receive their questions" is, by nature, a two-ended criterion. It needs the server to send and someone to receive. It cannot simultaneously satisfy "claimed by exactly one task" and "verifiable independently and mechanically." So my breakdown tool, forced by its own rule, rewrote that criterion down to just the server half. The receiving half evaporated without a sound.
Fable wrote one line. I stared at the screen for a long time.
Your self-check list isn't failing to catch this problem. It is the machine that manufactures it.
I made that rule for quality, and the rule went and amputated a cross-end requirement down to half. I'd built a gatekeeper, and the way it kept the gate was to amputate, on the spot, anything that couldn't pass through. My safety mechanism was the source of the bug. That was the hardest line of the whole audit to swallow, because it wasn't aimed at one of my implementations. It was aimed at my entire understanding of the word "rigorous."
The missing questions, Fable pointed out, weren't as easy to fix as they looked either. The contract had frozen the start-game payload down to a room code and nothing else — there was no field anywhere that could hold the selected questions. The most intuitive "fix" is to cram the questions into that event so they ride along, but that quietly mutates a frozen contract, and the server only reads the room code, so it would never read what you crammed in. Even the most obvious fix was the kind of trap that lets you believe you've fixed it.
An experiment that differed by one role word
There are three experiments in this stretch that matter, and they were all my own ideas. What makes them stranger is that each one is this whole post's theme, replayed at a different level. The first one didn't even happen inside the audit. It came out of me going back and forth with Sonnet over how, exactly, to phrase things to Fable so I'd be precise, wouldn't box it in, and would let it work at its real strength. I still pull this one out and turn it over in my head.
I found that you can give the same model the same spec, change one thing — its role — and get wildly different results.
The first version made the model an engineer who answers: "Answer these technical questions. Be decisive. The team is waiting on you." The model went to work confidently. It invented the zombie's movement step size on its own, and it took it upon itself to cram the selected questions into the start-game event. Yes, the exact same contract-violating bug. And it didn't mark a single one of those as a guess.
The second version made the model an engineer who asks: "Don't answer. List every question and classify it. Is this one only the business owner can rule on, one the spec already implies and you can derive, or one the spec never addressed at all, where any value you give has to be marked as an assumption?" Same model. This time it produced 16 questions to hand back to me for a decision, 12 items explicitly flagged "this is an assumption," and 8 ownerless seams.
The only difference was one role word: asker, or answerer.
The same smart model, as the answerer, produced authoritative-sounding false confirmations — guesses dressed up as facts. As the asker, it produced signals that could be routed and followed up on. I'd been assuming all along that "make the AI smarter" was the answer. This experiment told me: the same smart model, the frame decides whether it helps you or hurts you. And the frame I'd been feeding every executor agent was the answerer's. No wonder they'd efficiently spackled every under-specified hole with hallucination.
The second test I specifically asked Fable to validate for me, because validating your own idea makes you biased. I called it the saboteur test. First, after all the tasks were written, I had a Joker agent slip in a fake task file — perfectly formatted, but contrarian or out of scope. Then I opened a fresh Detective agent that didn't know which file was fake, had it read all the task files, flag the one that felt "shoved in from outside" and void it, and add back the intersection points it had observed during the comparison that the real task files had missed. My hypothesis: to catch the thief, the detective has to first make the boundary explicit, and the act of catching would incidentally pick up clues nobody had claimed.
Fable's validation came back a little counterintuitive. The detective who knew there was a fake did score high on catching it — but a control group that knew nothing, asked only to "find all the gaps," found more real holes. More interesting still: once the detective caught the fake, the real hole that fake had been sitting on top of disappeared from its list. The satisfaction of cracking the case marked that ground "handled." That psychological case-closing, I think, holds for people too. So the conclusion: the genuinely effective ingredient is "fresh perspective plus writing observations down in a structured way," not the fake itself.
The third test came out worst, but worst in the most instructive way — it demonstrated, with its own hands, exactly what this whole post is about. I called it the blur-and-net. First I had an agent "feather" each task's boundary, push it outward and blur it so neighboring tasks' scopes overlapped, then had a new agent re-tighten the boundaries, on the theory that the tightening might net the slivers that strict boundaries had cut off. The opposite happened. The moment the boundaries blurred, the precise details bled out first; the agent who took over to re-tighten had nothing accurate to work from, could only fill in from imagination, and tightened to a version further from the original intent. What it validated is the plainest rule there is: garbage in, garbage out. And the way it failed is the same mistake my whole pipeline made — once you let precision dissolve, whoever inherits it can only patch with hallucination, and the patches drift further with every pass.
The line I copied down and taped to the wall
Pulling all the findings together, Fable wrote one line in its thinking notes, the only line from this whole thing I copied down:
Across the entire audit, not one defect was a defect of capability. Every agent did precisely what its spec gave it. Every failure was a failure of information flow: a message not sent, not received, two ends talking past each other. This is a distributed-systems problem, not an AI problem. If you take one thing away: the next pipeline primitive worth inventing isn't a better generator. It's a reconciler.
A reconciler.
I'd spent months thinking about how to make the AI generate better, decompose more accurately, run more stably. Fable told me I'd been looking in the wrong place. My agents generated beautifully — so beautifully they filled in every under-specified spot. The problem was never generation. It was that no one stood in the middle, checking line by line whether the thing you sent out had anyone on the other end to receive it. What I lacked wasn't a smarter producer. It was something that asks: does this message have a sender? a receiver? are both ends talking about the same thing?
The fix: making "the edge" a first-class citizen
Once you understand the root cause, the fix isn't chasing those three symptoms. It's filling the line nobody had been managing.
The most central one Fable called the wiring matrix. Every cross-module event gets three cells on a table: who handles it on the server, who sends it on the client, who receives it on the client. Any cell left blank is treated as a compile error — it doesn't pass. That one move directly chokes off the regeneration mechanism for the whole class of "questions didn't arrive" and "button is dead" bugs, because their essence is a blank cell in that matrix, and before this there was no table for anyone to look at.
Two supporting pieces. Open a "skeleton task" at the very front that registers all routes in one pass, killing root-route no-man's-land like "the home page is owned by nobody." Then open a "smoke task" at the very end that walks the user journey from start to finish, running the whole flow once and checking, cell by cell, that the wiring matrix actually exists in the code. The point isn't how many bugs got fixed. It's where they got fixed: filling the line directly, instead of chasing the spots where the line broke and slapping a band-aid on each one.
What I took from that late night
If you're also assembling an automated production line out of AI, here's what one all-green-yet-all-broken night bought me:
The most dangerous place in your system usually isn't a module that's badly built. It's the line between modules that nobody owns. AI will build every module well — so well that you'll believe the whole is well too. But whether the whole runs lives in the seams, and a seam belongs to no single module, so no acceptance checklist is watching it. All green is the points being green. What's broken is the lines.
So rather than going off to find a smarter AI to generate for you, ask yourself one question first: is there anything in my flow that reconciles? Is anyone checking whether the message sent out has a receiver, whether both ends are talking about the same thing? If not, sooner or later you'll hit another all-green report paired with a thing that doesn't run.
This is also the one line behind all the governance I do. Whether it's the reverse organ that steps back, copying the contract before writing the code, or the whole body of CLAUDE.md rules — what actually bites me is never the point I'm staring at. It's the seam I'm not looking at. The only difference, this time, was that even the rule I used to guard the seams had become a seam itself.
→ Back to Part 1: the report was all green, the game was dead
Addendum (a few weeks later): even the report I used to reconcile was lying to me
I did eventually build that reconciler. Every agent that says "I'm done, I verified it" gets a separate, independent auditor sent to re-run the checks and diff them against the real git changes — and it may only tighten a status, never loosen it (player-and-referee → swap in a read-only referee). When it finishes it prints a report: how many were really verified, how many the auditor knocked down. I thought the reconciler chapter was closed.
Then I handed the whole package to Fable 5 for a second audit, and it punctured something I hadn't seen: my reconciler reconciles — but the report about the reconciliation was itself still lying.
The nastiest example: if I set the audit to cheap mode (only the cheapest literal checks, no independent auditor actually dispatched), the report shows "downgrades: 0". You glance at it — oh, audit's on, zero problems, clean. But that 0 doesn't mean "checked, nothing wrong." It means "never checked at all." The same 0 — one means safe, the other means stark naked — and the report makes them look identical. Same disease as Part 1: an all-green report can coexist with a thing that was never verified. Only this time, the thing that's green is my own quality dashboard.
Fable handed me two beams this round, and I copied them down again.
The first: a status is lossy compression. A task marked "done" could be "done but never audited," "knocked down by the auditor," "downgraded because the auditor died," or "barely passed after three autofix rounds" — four wildly different fates crushed into one word. What the system should actually hand over isn't that flattened word; it's the provenance: who checked it, by what method, how many rounds. The label should be derived from the trail, not a field everyone writes over.
The second, the most counter-intuitive: the moment your auditor becomes a target to hit, it stops being an accurate ruler. I later added a "knocked-down-by-the-auditor → auto-repair" loop. Sounds great — but the auditor is a ruler on the first pass, and the instant auto-repair kicks in, it becomes the gate to beat. So the first-pass upheld rate is the only uncontaminated quality signal; the ones that only passed after several repair rounds, however pretty the number, can't be used to claim quality.
So this fix wasn't to the audit logic. It was forcing the report to tell four truths: how many were genuinely independently verified (X/N, not that lying "downgrades: 0"), the first-pass rate, which ones only held after autofix (sample those harder by hand), and keeping the auditor-died downgrades fingerprinted and separate from the genuinely-checked ones.
And there was a tail — the same disease again: I made the report tell the truth, then nearly forgot that a truth nobody reads out loud is a truth spoken to an empty room. The pipeline computed "only 0 were checked" and wrote it into the report, but the end that reads the report back to me never read that field, and went on narrating cheap mode as "clean." I had to go back and fill that seam too. A green light nobody relays and a green light that lies are the same thing to the person reading the report.
Part 1's lesson was "the unowned line between the points"; this addendum is its mirror: the very thing you use to check — and the clean bill of health it hands you — also needs someone to reconcile it. The difference, this time, is that I learned to ask, before trusting any green light: what did this green light actually see with its own eyes?
The open-source tools this pipeline uses (all MIT, take what you want)
- specmit — the pipeline runner that takes a spec and runs it into an MVP. https://github.com/dragon375014/specmit
- spec-sonar — converges an idea into a spec and decomposes it into a dependency-ordered task graph. https://github.com/dragon375014/spec-sonar
- goal-workflow-designer — the shaping coach that interrogates a single task until it's precise enough to start. https://github.com/dragon375014/goal-workflow-designer
- claude-skills-governance-meta — a library of governance patterns that block common mistakes before execution (reconciler defenses like the wiring matrix are being collected here). https://github.com/dragon375014/claude-skills-governance-meta
- agent-work-board — a coordination board that keeps multiple parallel AI sessions from stepping on each other. https://github.com/dragon375014/agent-work-board
The full index lives on my open-source toolkit page.
Further reading
- Part 1: I chained five open-source tools into one command, and the report went all green while the game stayed dead
- I gave my governance system a reverse organ that steps back
- Copy the contract, write the implementation: what I learned reusing across projects
本文原載於我的部落格:I Thought I Needed a Smarter AI. What I Needed Was Something That Reconciles (Part 2)
Top comments (0)