How to Build Bounded Agents
Part II: How to Build Bounded Agents
One module, one question, six local artifacts, and a coordinator that knows where the system can fail
What this article covers
- Why directory structure is not a real boundary until decisions, inputs, outputs, prohibitions, and evidence are explicit.
- How one-question modules contain errors and make refusals diagnosable.
- Which six local artifacts turn a module into a verifiable unit of work.
- What a coordinator should validate, what it should not reinterpret, and where human release remains necessary.
- When splitting work improves fault isolation, and when coordination cost makes the system worse.

The first part explained why a large context and an expensive model did not rescue an anonymized route optimization project. The agent could write code. The system could not explain which decision had removed a viable route.
This part is the engineering answer.
The short version is simple: do not divide an agentic application by nouns. Divide it by decisions.
"Everything about orders" is a monolith. Order collection, order acceptance, duplicate handling, route search, route economics, and session release are different decisions. They may read the same rows. They should not share responsibility.
The directory is not the boundary
Creating folders does nothing by itself. If every agent can read every folder, change every table, reinterpret every number, and call every tool, the project is still one large room. The walls are decorative.
A directory becomes a real boundary when it defines four things:
- The question answered by the module.
- The exact input and output.
- The decisions the local agent is forbidden to make.
- The evidence required to verify its result.
This is old software engineering with a new consumer. In his 1972 paper, On the Criteria To Be Used in Decomposing Systems into Modules, David Parnas argued that modules should hide design decisions likely to change and allow one part of a system to be understood with limited knowledge of another. He warned against decomposition based only on the sequence of processing steps.
For agents, the same principle becomes physical. Every extra file, neighboring decision, stale rule, and unrelated tool consumes context and creates another action the agent might take.
So the folder has to act like a workbench. The agent sees the part, the drawing, the gauges, the permitted tools. It does not get the keys to the whole factory.
Rule one: one module answers one question
The acceptance module asks:
Is this order card readable and physically admissible?
The economics module asks:
Does this assembled route meet the economic release criteria?
The route builder asks another question. The collector asks another. The coordinator does not answer all of them again. It checks whether their outputs can be assembled without contradiction.
This gives us a practical test. If a module's question needs and, inspect the sentence. "Parse the card and decide whether the route is profitable" contains two decisions that operate on different evidence and fail for different reasons.
The boundary follows the decision, not the data entity. Grouping everything that touches an order into one place produced more than 8,000 lines of SQL, search, ranking, economics, and session lifecycle. The code was close together because the rows were close together. The reasoning was not.
Rule two: a module must be allowed to be locally wrong
Here is the strongest boundary test I know:
Can this module make a mistake without making every other module wrong?
Order acceptance may pass an economically terrible order and remain correct. An order paying roughly $350 for almost 900 miles is physically admissible. Its standalone rate is irrelevant because the economics module may later pair it with another vehicle.
That local independence is useful. If the acceptance agent rejects the card, we inspect evidence visibility, vehicle type, vehicle count, location resolution, or capacity. We do not inspect fuel cost or route revenue.
A monolith cannot be wrong locally. Its answer mixes several decisions, so one wrong assumption contaminates the final result and erases the location of the error.
The goal is not to eliminate error. It is to contain it.
Rule three: shared numbers need one owner
If two modules must agree on one derived value, that value belongs to a third shared component imported by both.
We learned this from a bug where the same quantity was calculated in three places, three ways. The results differed in the final bits. Each calculation looked reasonable. Together they broke identity and state comparison.
Do not solve this with a paragraph that tells three agents to "calculate consistently." Give the calculation one implementation, one version, one test suite, one owner.
Numbers are contracts.
The six local artifacts inside a bounded module
A working module folder contains six local artifacts. None was added for appearance. Each one corresponds to a real loss.
order_acceptance/
CONTRACT.md
AGENT.md
INVARIANTS.md
DECISIONS.md
FAILURES.md
tests/
CONTRACT.md
This file states the exact input and output, including contract versions.
Without it, a value can spread across three implementations and nobody notices that the definitions differ. A contract should say which fields are required, which may be absent, how partial is represented, which reason codes are legal, and whether the output is append-only.
The wording should be precise enough to generate validation code from it.
AGENT.md
This is the local operating instruction. It lists allowed decisions, required checks, and prohibited decisions.
The prohibited list matters most.
For order acceptance, the agent may compare vehicle-count evidence. It may reject an unsupported carrier type. It may return partial when sources conflict. It may not change price, miles, or vehicle composition. It may not build pairs, calculate a route, request another board query, or guess which contradictory source is correct.
One instruction is mandatory:
You are allowed to return "I do not know."
Without a defined refusal state, an agent will often complete the pattern and produce an answer. In a production system, uncertainty has to be an output type.
INVARIANTS.md
This file contains properties that must always hold, written in a form that can become a test.
"The route should finish inside the target zone" is documentation. A test that fails when the final coordinates fall outside the target zone is an invariant.
The project had carried that sentence for a long time. Nothing enforced it. Then the system released a route ending in another city.
If an invariant cannot be tested, it is probably still an intention.
tests/
Tests live beside the module instead of only in one central pile.
In one audit, dozens of test files were collected together. Several modules still had no test coverage at all. One had recently crashed a live session. Another generated the operator's confirmation text.
Local tests make missing coverage visible from the directory tree. They also let a local agent verify its own decision without loading unrelated test fixtures and system history.
DECISIONS.md
This file records why a rule or threshold exists, when it was introduced, and which later decision may have replaced its reason.
An old minimum of roughly 100 miles for a loaded leg survived after the application gained whole-route economic validation. The original reason expired. The threshold did not. It blocked a viable route because one leg was about 60 miles.
Code tells you what a rule does. A decision record tells the next agent whether the rule still deserves to exist.
FAILURES.md
This file records real failures, reason codes, and links to reproducible sessions.
Most teams keep examples of success and delete rejected candidates. I keep refusals append-only. That is why one grouping query exposed more than 1.4 million rejections tied to a single-occupancy limit.
The output did not say "no route found." It told us which rule had killed every route candidate.
That is observability.
What the local agent sees
The local acceptance agent receives:
- its complete module folder;
- input data matching the declared contract;
- the confirmed run specification;
- the ZIP resolver required for location checks.
It does not receive neighboring modules, route economics, current search state, or permission to write arbitrary changes into the shared database.
This is not about distrusting the model. A decision made with unnecessary context is difficult to reproduce because the hidden influence of that context is difficult to isolate.
The acceptance result should be the same whether the future route is profitable or terrible. If route economics changes acceptance, the boundary has leaked.
What the coordinator should do
The coordinator is not a larger agent allowed to improvise across the whole repository. It is a controlled synthesis point.
In the workflow I use, every module is analyzed separately after a release candidate. The coordinator collects those local reports, checks the combined state, and produces one system-level analysis. I then review the result manually. Corrections become another version.
The coordinator should receive contracts, outcome summaries, failure distributions, version identifiers, and cross-module invariants. It should not need every token of every local investigation.
Its work is closer to dispatch than production:
- confirm that each required module returned an outcome;
- confirm that contract versions match;
- detect contradictions between local results;
- run system-level invariants that no single module owns;
- preserve the evidence used for release;
- stop when a required module returns
partialor an unresolved conflict.
This structure also matches current multi-agent evidence. Google's agent scaling study found that a central coordinator contained error amplification better than independent agents. The 2025 MAST study found recurring failures in specification, inter-agent alignment, verification, and termination. Coordination is useful when it acts as a validation bottleneck. It is dangerous when it becomes another source of untraceable decisions.
Commits and releases follow decision stages
Changes are committed by stage, and stages are not mixed casually.
Every task explicitly states whether the change affects observable behavior. This matters because an apparent internal cleanup can alter state identity.
We once had the same quantity calculated in several places. Replacing those calculations with one function looked like a refactor. It also changed state signatures stored in a unique index. If the change had shipped as "refactoring with no behavior change," session resumption could have broken without a migration path.
Separate commits are not ceremony here. They preserve causality. When a release changes behavior, we need to know which decision changed it.
How small should a module be?
There is no universal line count, token count, or directory depth.
The research gives a warning, not a magic number. Google's study found that multi-agent systems helped parallelizable tasks but hurt sequential planning by 39 to 70 percent. Tool-heavy tasks also paid a larger coordination cost. If every result must be passed through several agents before the next step can begin, the architecture spends its budget talking to itself.
I use four tests instead:
- Can the module's question be written in one sentence?
- Can its output be validated without reading the implementation of neighboring modules?
- Can it fail with a specific reason code without corrupting the rest of the system?
- Can another module consume its output through a versioned contract?
If the answer is no, the module is either too large or its boundary is in the wrong place.
A module may be too small when it cannot produce a meaningful, independently testable outcome, when most of its context is spent reconstructing the previous step, or when the work requires one continuous chain of reasoning. In that case, splitting adds messages and loses state without gaining fault isolation.
The smallest useful module is not the smallest piece of code. It is the smallest complete decision.
Current engineering practice is moving in the same direction
This pattern is appearing in serious agent systems.
OpenAI's 2026 report on harness engineering describes a repository where a short AGENTS.md acts as a map, while structured documentation is the source of truth. The system uses progressive disclosure, strict dependency directions, architectural tests, and automated checks for stale documentation. The team explicitly reports that one giant instruction file failed because it crowded out the task, made every rule look equally important, and became difficult to verify.
Anthropic's March 2026 report on harness design for long-running application development describes decomposing builds into tractable chunks, carrying state through structured artifacts, and separating a generator from an evaluator. The point of the evaluator is not to write more code. It owns a different decision.
In another experiment, Anthropic used sixteen agents over nearly 2,000 sessions to build a 100,000-line C compiler capable of compiling Linux 6.9 for x86, ARM, and RISC-V. Agents claimed tasks through explicit lock files and worked on separate failures. The project cost about $20,000 in API usage, so it is not a recipe for every application. It is a useful proof that parallel agent work needs explicit ownership, shared tests, and a mechanism that prevents several agents from solving the same problem at once. Building a C compiler with a team of parallel Claudes
The vendors differ. The engineering pattern does not: maps instead of encyclopedias, local responsibility, structured handoffs, tests, visible state, independent evaluation.
The architecture in one picture
The application is moving toward this structure:
board collector
-> sealed evidence database
-> order acceptance
-> candidate search and pairing
-> route construction
-> whole-route economics
-> coordinator analysis
-> human review
-> versioned release
Each arrow is a contract. Each module owns one decision. Each refusal remains available for analysis. The coordinator sees enough to assemble the system, not enough to silently rewrite every local result.
This is the same operating logic I now use when building agents, applications, information-processing systems, and large selection pipelines. Split the flow where decisions differ. Keep deterministic work deterministic. Use AI where evidence has to be interpreted. Make uncertainty legal. Store the rejected paths. Put one coordinator above the local modules and keep a human at the release boundary.
The best agent is not the one that knows the entire project.
It knows exactly which question is theirs.
Practical takeaways
- Write the module's question in one sentence. If it needs two decisions, split it.
- Version the input and output contract, including legal refusal and
partialstates. - Give every shared derived number one implementation, one owner, and one test suite.
- Keep invariants, tests, decision history, and failure evidence beside the module that owns them.
- Let the coordinator validate contracts and cross-module state. Do not let it silently redo local decisions.
- Keep a human at the final release boundary when the outcome changes real operations.
In brief
- A bounded agent is defined by a decision boundary, not a folder name.
- Local errors are useful when they remain local, visible, and reproducible.
- Refusal and uncertainty are output types, not conversational failures.
- Coordination earns its cost only when it reduces contradiction and preserves evidence.
- The smallest useful module is the smallest complete decision.
Sources
Research papers and engineering reports referenced in the series:
- Lost in the Middle: How Language Models Use Long Contexts, Liu et al., 2023.
- RULER: What's the Real Context Size of Your Long-Context Language Models?, Hsieh et al., 2024.
- NoLiMa: Long-Context Evaluation Beyond Literal Matching, Modarressi et al., ICML 2025.
- Context Length Alone Hurts LLM Performance Despite Perfect Retrieval, Du et al., preprint, 2025.
- Why Do Multi-Agent LLM Systems Fail?, Cemri et al., preprint, 2025.
- Towards a Science of Scaling Agent Systems, Kim et al., preprint, 2025. See also the Google Research summary, January 2026.
- On the Criteria To Be Used in Decomposing Systems into Modules, D. L. Parnas, Communications of the ACM, 1972.
- How we built our multi-agent research system, Anthropic Engineering, June 2025.
- Harness engineering: leveraging Codex in an agent-first world, OpenAI, 2026.
- Harness design for long-running application development, Anthropic Engineering, March 2026.
- Building a C compiler with a team of parallel Claudes, Anthropic Engineering, February 2026.