· DYNOKORE TEAM
Our coding agent stopped three times on one task. Here's every question it asked.
On 22 October 2026 a coding agent picked up a task in DynoInvoice: counterparty master data for the master-data service. Resolve a buyer or seller named on an invoice, record where its identifiers came from, keep its history, and flag whether it belongs to the same GST group or the same customer account as the business filing the invoice.
Overnight, the agent stopped three times. Each time it put design questions on the record instead of guessing, the task waited while other work carried on, and it resumed once the questions were answered. By early afternoon the task was done and merged. Here is every question, and who answered it.
The requirement behind it
The requirement says: when a document names a counterparty, flag sameGroup and sameTenant. Whether supplies inside a GST group have to be reported is still waiting on an answer from a tax adviser, so the rule for them is defined as provisional data in a rules pack, which can be changed by publishing data, without changing code.
That requirement has been revised as decisions landed: once when the owner ruled that build waves follow dependencies, once when the provisional part was narrowed to same-group supplies only and a non-blocking warning was added, and twice more in October as the coding task exposed gaps.
Stop 1: the contract didn't say enough
The contract had no legal name or address for a counterparty, so those were added, with a list of findings when they're missing. Nothing recorded where an identifier came from, which became an optional inline source object. There was no version history either, so a paged history operation was added.
The fourth question mattered most: where do the two flags come from? The ruling was a read model the service owns, fed by two organisation-structure events. A synchronous call to the tenant service was rejected, because an earlier published rule makes this a leaf service that doesn't make calls.
Stop 2: the edges of the data
An event needed to say which entity it was about, so the request got an optional entityRef. A 21-character scheme code didn't fit a 20-character field, and the limit went up to 32. Individuals couldn't be looked up until a partyRef parameter was added. And there was no operation to write ingested counterparties, so one was added on an existing service edge.
These followed a ruling the owner had made the same day: "Who may call whom, and when, must follow the system architecture design, not a mechanical judgement."
Stop 3: where a refusal comes from
The contract said callers outside their scope get a 403, but not where that check lives. The ruling was one generic runtime filter driven by the contract. Inferring scope from property names was rejected, because a guess in a security filter either lets requests through or refuses too many.
All nine questions were ruled by the coordinating agent, and the rulings at two of the three stops cite an earlier ruling as precedent. None went to the owner: under the owner's standing rule, questions that don't touch functionality or direction are ruled by the coordinating agent when a published rule or precedent settles them.
Where a person did step in
A week earlier, the agents had found that the first-wave contracts lacked identifiers the second wave would need, and the recommendation was a dedicated follow-up task. The owner chose differently: "Contract-first means the contract is settled before it's implemented. It doesn't mean it has to be complete in the first wave. Add to the contract when it's needed." That ruling is why the contract was extended right before this task, and it is on the record with the owner's name on it.
What review caught, and what we'd flag ourselves
The day before, a revision of the task was sent back with four findings: its context still cited a deleted criterion; "the set held by the policy port" didn't say which set, so it couldn't be checked; a criterion said no merge request existed when one did; and an inline example used a request body the service now rejects. All four were fixed before it was approved.
One thing we'd point out ourselves. One acceptance criterion says two entities in the same account record a supply and a purchase that pair up. Its test checks the flags. The pairing belongs to another requirement, so the test's name alone doesn't prove pairing works. You can read the three tests and the first commit in the public repository.
If your agents build from a spec
- Let the agent stop, and make stopping cheap. Only the task that hit the gap should wait. If a stop freezes everything, agents learn to guess.
- Ask for options and a recommendation. "Which of these, and why" gets ruled fast. "What should I do" doesn't.
- Write down who decides what, in advance. Here, anything about what the product does, its direction, or loosening a check goes to a person. Contract shapes and data limits don't.
- Answer from precedent. Keep rulings where the next question can cite them, so the same question isn't argued twice.
- Ship undecided business rules as data, marked provisional. The service code can be finished while the tax question is still open.
How the record holds it together
In Keel, the requirement, its acceptance criteria, the task and every design question are linked records. A coding agent that hits a gap raises a question with options; only that task waits; the answer is recorded with who decided, and rulings are kept so a later question can be answered from them. As of 26 October 2026, DynoInvoice's coding agents had stopped about 127 times on about 113 tasks this way, raising 281 questions. The task is marked done from its merged change and CI verdict, not from the agent's report.
More examples, each traced from requirement to merged code: How DynoInvoice is built, commit by commit.
Written by the DynoKore team with AI assistance. Events from DynoInvoice's public repository and Keel record, October 2026. Owner quotes are translated from chat. We're opening DynoKore Keel to a small group of design partners: dynokore.ai.