DynoInvoice · When the spec runs out
The most expensive thing a coding agent does is fill a gap in the specification with a confident guess. It compiles, the tests it wrote pass, and the wrong behaviour surfaces weeks later. In DynoInvoice, a coder that reaches a gap stops, raises a design question with options, and waits for a ruling. Here is what that looks like, with one full example and three short ones.
On 22 October a coding agent was building the counterparty master in DynoInvoice’s master-data service (task INV-T-4943). The task said the service returns a counterparty’s legal name and address, plus the outcome of resolving it, including three blocking codes. The shared API contract had no field for a name, none for an address, and nowhere to carry a finding. The task also said what to do in that situation: if the work needs a contract change, stop and offer options.
| Time (SGT) | What happened | Record |
|---|---|---|
| 10:45 | The coder stops; the task is marked blocked on a design question | INV-Q-8421 |
| 10:53 | Ruling published (8 minutes) | INV-CS-8428 |
| 10:56 | Task revised: a new dependency, and the contract fields named | INV-CS-8432 |
| 12:20 | The shared contract is changed by its own task, reviewed like any other | adde2021 |
| 12:30 | The coder resumes | — |
| 13:30–20:06 | Service code lands, through review and two fix rounds | 1de1ed9f … 07e79ef3 |
A (recommended): add optional name and address properties and an optional list of findings; the answer stays 200; a separate contract task carries the change. B: answer 422 with the code, as an existing error already does. C: keep a single free-text address.
The coordinating agent chose A. The owner had already ruled the facts underneath: name and address are master data, and contracts are supplemented when needed. The practice named was additive, backward-compatible change within a major version, so the breaking-change gate stays green with no exception. The ruling records that it widens or narrows nothing the owner decided and relaxes no check.
What a guess would have produced. Each of these passes a local build and fails later, in review or in another service:
The same afternoon the coder stopped once more, on request fields and a lookup; it was ruled in seven minutes (cfe12822). The task was done that evening.
Building consent for software agents, the coder found six permissions with no published scope, such as viewing document content or exporting an audit pack. That is policy, so it stopped. Until the owner decided, all six stayed refused to agents. The owner accepted the recommendation: three read permissions now require a specific scope, and the other three stay refused to agents until a requirement names one (d3c1ca2e, 41e413e8). A guess either way would have been a security decision made by the agent.
The message relay needed a database lock in one transaction and per-tenant work in another, and the transaction runner refuses a nested transaction under a different tenant. Every way out meant relaxing that refusal, changing a concurrency rule or adding an exemption, so the coder stopped. The owner chose a narrow opening in the tenancy kernel used only by the relay (058f6c4a … d6c45165). A guess would have quietly weakened a tenant-isolation guard.
A scheduled job that runs across tenants had no tenant to record its runs under. The options were a null tenant, a sentinel tenant, or one record per tenant. The owner wrote a fourth design instead: a reserved platform tenant id that is never provisioned, can never come from a token, and never appears on the wire (f4a5016a). A guessed sentinel would have put a fake tenant into every service’s outbox and every consumer.
Stopping is written into the work, not left to the agent’s judgement. Each task says what done means and what to do when the work needs something the specification doesn’t give, such as a contract change. A blocked task is marked in Keel, other work carries on, and the question is routed by its kind: functionality, direction and any relaxing of a check go to the owner; clear-cut technical choices are ruled by the coordinating agent on a named practice or an earlier ruling. While a policy question waits, the safe default holds.
This doesn’t mean an agent never guesses. It means every gap that reaches the record has a question, options, a ruling and a revised task, and review catches gaps a coder missed. The stops are cheap: the median from stop to resume is a little over two hours, and most of the 281 questions never needed the owner.
More: when green isn’t enough · why does this line exist? · reviewing like an attacker · when a decision changes · one requirement and everything recorded about it · eight traces