Here is a search endpoint returning an empty array. A human developer looks at it and immediately starts asking the right questions: is my query wrong, is the index empty, is the filter too narrow, did the service just come up? They open a second tab. They check a dashboard. They ask someone.
An agent does none of that. It reads [], concludes "there is nothing", and acts. If the array was empty because the index had not finished building, the agent has just been told a confident lie by a service that did nothing wrong by conventional standards.
That gap is the whole subject of this post. Everything below follows from one premise, and it is worth stating plainly before the consequences:
An agent puts a question to a service and acts on the reply immediately, unsupervised, and cannot see what produced it. So the reply has to carry it: an answer is a claim about its own reliability, and the service is accountable for that claim as much as for the content.
I wrote this premise up as a closed normative corpus of fifteen areas while building Hilum Tools, because I kept re-deriving the same rules in each subsystem and getting them subtly different each time. What follows is the part that transfers.
Why the usual contracts are not enough
REST, GraphQL and typed RPC all describe shape. They tell the caller that the response is an array of objects with these fields. They say nothing about whether the answer is complete, how old the underlying data is, whether the search actually ran, or how confident the service is that this is the right answer.
For a human consumer that omission is survivable, because the human supplies the missing judgement. They know the deploy went out an hour ago. They notice the results look thin. They have context the protocol never carried.
An agent has no such side channel. Whatever the response does not say, the agent will fill in with the most plausible assumption — and "most plausible" is exactly how confident wrongness is manufactured at scale. The protocol has to carry the judgement, because there is nobody left to supply it.
Five rules that fall out of the premise
The full corpus is longer, but almost everything practical descends from these.
1. Emptiness must be typed
[] is the single most dangerous response in a machine-facing API, because at least four different situations produce it:
- The search ran, the corpus is healthy, nothing matched. Genuine absence.
- The search ran against an index that is empty or still building. Unknown, not absence.
- The search was skipped — the backend was unavailable and the error was swallowed upstream. Failure wearing absence as a costume.
- The filter excluded everything before the search happened. Absence, but of your own making.
A human distinguishes these by intuition and investigation. An agent needs them distinguished in the payload. Every empty result should carry which of the four it is. This is a small change to a response type and it removes an entire class of confident error.
2. Every answer states its coverage and its age
Two fields, and they are the difference between an answer an agent can plan around and one it can only trust blindly.
Coverage — what fraction of the intended universe was actually consulted. "I searched the whole tree" and "I searched the eleven files that were indexed before the crash" are wildly different answers with identical shapes.
Age — how stale the material behind the answer is. Not the response timestamp, the data timestamp. An index that stopped updating forty minutes ago answering with total assurance is worse than one that says so, because the second one lets the caller decide whether forty minutes matters for this question.
3. "No" and "I do not know" are different answers
Conflating them is the most common failure in this category and the one with the worst consequences, because the two demand opposite responses from the caller. "No" means stop looking here. "I do not know" means look somewhere else, or ask again later, or escalate.
Give them separate representations. Then make the "I do not know" path cheap to produce and cheap to read — if a service has to work hard to admit uncertainty, it will quietly stop admitting it under load, which is exactly when you need it most.
4. Every answer states what it cost and what it left out
An agent works against a budget it cannot see from inside your service. If a response is a selection — truncated to a limit, capped by a token ceiling, sampled — the response must say so, and say roughly what fell outside.
"Three more matched but did not fit" is a fact the agent can act on: it can raise the budget, narrow the question, or note the incompleteness in its own output. Silent truncation reads as completeness, and an agent that believes it has the whole picture will stop looking.
5. Refusals must be actionable
When a service declines — rate limit, permission, malformed input, unsupported operation — the reply needs to say what would make it succeed. Not an error taxonomy for a human to look up in documentation the agent will not read: the corrective action, in the payload.
A human hits a 403 and asks a colleague. An agent hits a 403 and either retries the same call, gives up on a task that was achievable, or invents a workaround. All three are worse than being told "this needs the write:memory scope, which this token does not have".
Why the model names no technology
The corpus I wrote deliberately mentions no language, no storage engine, no framework and no product. Fifteen areas — the answer contract, the cost of an answer, provenance, observability, verdicts, access, coordination, federation, distribution and the rest — each stated as what must hold, never as how to build it.
That constraint is not stylistic. It is what makes the model liftable into a project built out of entirely different machinery. A rule that says "return a RetrievalEnvelope with a coverage field" is advice about one codebase. A rule that says "an answer states what fraction of the intended universe it consulted" is a rule you can carry into a Python service, a Go gateway, or an API you did not write.
Portability like that decays the moment nobody is checking, so it is enforced mechanically: a build guard scans the corpus for technology nouns and fails when one appears. The discipline is not a habit anyone has to remember.
How to adopt this without writing fifteen documents
Start with the premise, not the corpus. One paragraph, written down, about what your consumer can and cannot do with your answers. Then work forward:
- Audit your empty and error responses. For each one, ask what an unsupervised caller would conclude and whether that conclusion is warranted. This alone usually surfaces several real bugs.
- Add coverage and age to your most-consumed endpoint. One endpoint, two fields. Measure whether downstream behaviour improves before rolling it out further.
- Split "no" from "unknown" everywhere the distinction exists in reality.
- Write down the premise and the rules it produces, in your own words, naming no technology. Keep it short enough that people read it.
- Enforce mechanically what you can — a schema check, a lint rule, a test that a response envelope carries its metadata.
The point is not to produce documentation. It is that once the premise is written down, arguments about individual endpoints stop being matters of taste. Somebody proposes returning a bare array, someone else points at the premise, and the conversation is thirty seconds long instead of a design review.
Takeaways
- The consumer's inability to look behind the answer is the design constraint, not a detail.
- Shape contracts describe the payload; machine-facing services also need to describe the answer's reliability.
- Type your emptiness. Four different situations produce
[]and they demand different responses. - Every answer carries coverage and data age, not just a timestamp.
- "No" and "I do not know" are different answers with opposite implications for the caller.
- State what was omitted and what the answer cost — silent truncation reads as completeness.
- Refusals carry the corrective action, not just a code.
- Write the rules naming no technology, and enforce that mechanically, or they stop being portable within a quarter.
The concrete version of this — retrieval that reports its own budget and index age — is token-budgeted retrieval. The version that applies to your own architecture record is the decision graph. If you want a second opinion on how your services will behave once agents are the consumers, that is what a consultation is for.
