I was working on a change across three projects: a UI project, a backend rules engine (Backend A), and another backend service (Backend B). I already had a separate coding-agent session open in each project’s workspace.
As I worked through the change, I had to carry technical context and questions between those sessions. One agent would uncover a dependency; I would take that finding to the other project sessions and bring their answers back. I was doing the engineering work and coordinating the discussion by hand.
I wanted to see whether the sessions could share that work through a small extension for my
coding agent — a command called /discuss. What I learned building it turned out to be more
interesting than the tool itself.
The short version: I expected to build an orchestration engine. I ended up building three small tools and letting the model be the loop.
The idea: give each project a stand-in
The setup mirrors what I already had. Each project gets an AI stand-in. Each stand-in runs inside its own project’s workspace, so it can inspect the real code before answering instead of guessing from the service name.
Then there’s one more participant: a facilitator — the coordination I was doing by hand. It knows no codebase. Its only job is to run the meeting — decide who speaks next, notice when people are talking past each other, and write the summary at the end.
So the “meeting” has four seats:
YOU (ask the question)
│
FACILITATOR (runs the meeting)
│
┌───────────┼───────────┐
▼ ▼ ▼
UI project Backend A Backend B (each runs in its own project workspace)
Where the meeting happens
Everything is written to a single, plain markdown file — like a shared chat log. Every time a stand-in speaks, its comment is added to the bottom, signed with its name:
---
[Backend A]
I checked the code. This change is safe on our side, but it assumes the UI
sends a country code, and right now it doesn't. @UI project — can you confirm?
That’s the whole record. Nothing is hidden in a database you can’t find. You can open the file
at any time, scroll it like a chat, and see exactly who said what, in order — including when
one stand-in tags another with an @mention to ask a direct question.
How one round works
A single round is simple:
- The facilitator looks at the conversation so far.
- It picks one stand-in to speak next — usually whoever was just asked a question.
- That stand-in reads the whole document, checks its own code, and appends a reply.
- The facilitator reads the reply and decides what happens next.
Drawn out, the round looks like this — and notice the pivot is the facilitator’s decision, not a rule in the code:
┌───────────────────────────────────────────┐
│ Facilitator reads the discussion so far │
└───────────────────┬─────────────────────────┘
▼
Picks ONE stand-in to speak
▼
Stand-in reads the doc, checks its code, appends a reply
▼
┌───────────────────────────────────────────┐
│ Facilitator reads the reply and decides │
└───────────────────┬─────────────────────────┘
┌─────────────┴──────────────┐
▼ ▼
more to discuss? everyone quiet?
└── loop back to top Facilitator writes the summary
This repeats until every stand-in has said a fixed phrase that means “I have nothing more to
add unless someone asks me directly” — literally I AM HAPPY FOR NOW!!!. When everyone has
gone quiet, the facilitator writes a plain-English summary — the decision, and who needs to do
what — and the meeting is over.
The surprising part
Here’s what I expected to build: an orchestration engine. Something with a loop that tracks whose turn it is, detects when the discussion has converged, routes the questions people ask each other, and knows when to stop.
Here’s what I actually built: almost none of that.
The code only knows how to do three small things — think of them as three verbs:
- Speak — make one named stand-in read the document and add a reply.
- Status — report how many rounds have happened and whether everyone has gone quiet.
- Summarize — write the final summary to the document.
That’s it. Nothing in the code decides who talks next. Nothing in the code understands what
“converged” means. Nothing in the code reads the @mentions and routes the questions.
All of that — the actual running of the meeting — is the facilitator model’s own reasoning. It reads a reply, sees “@UI project,” and decides the UI stand-in goes next. It notices everyone has gone quiet and decides it’s time to summarize. The intelligence lives in the model, not in the code.
But the code isn’t doing nothing
It would be too neat to say “the code is dumb and the model does everything.” The code isn’t
running the meeting, but it is doing design work — through the instructions it hands each
stand-in. Each one is told: read the shared document, speak under your own name, use
@mentions when you need another project, and say the fixed phrase when you have nothing more
to add. That scaffolding is what makes the conversation hold together. The leverage didn’t
disappear — it moved from control flow to interface and prompt design.
Why that’s the whole trick
When people build with AI, the instinct is to write the workflow in code: first do this, then check that, then loop until this condition. It feels responsible. It’s also often a mistake, because you end up hard-coding a rigid process around something whose entire strength is flexible judgment.
The model is already a loop. It reads, it thinks, it acts, it reads the result, it thinks again. If you wrap that in your own control flow, you’re putting a rigid box around the one part of the system that’s good at handling the unexpected.
So the design that worked was almost the opposite of an engine:
- Give the model a few clean tools (the three verbs).
- Give it one shared source of truth (the document everyone reads and writes).
- Give each stand-in clear instructions, then let the model’s own reasoning be the loop.
The code got smaller, the behavior got more flexible, and — because the whole conversation lives in one readable file — the system stayed completely transparent.
The code that is worth writing
This doesn’t mean “write no code.” It means the code’s job changes. It stops trying to think and starts trying to keep the thinking safe and bounded.
In my extension, the only real control logic left is a couple of guardrails:
- A round counter that pauses every nine rounds and asks me whether to keep going — so it can’t quietly run for an hour and spend real money.
- A “has everyone gone quiet?” flag, so the facilitator knows when it’s allowed to wrap up.
That’s the useful division of labor. The model handles the judgment: who speaks, what matters, when we’re done. The code handles the limits: count the rounds, track the flags, call time.
Let the model be the loop
If you’re building on top of an AI model and you find yourself writing a big loop to control it, pause and ask a smaller question first: what if the model is the loop, and my code just hands it a few good tools, a shared place to work, and clear instructions — and keeps the minutes?
For a whole class of problems, that’s not a shortcut. It’s the better design.