Your Project Structure Is Part of the Agent Harness
When we talk about AI agent harnesses, we usually talk about the things immediately surrounding the model.
Prompts. Tools. Permissions. Guardrails. Context management. The loop that lets the agent look at what happened, take another action, and keep going.
That definition makes sense. But the more I work with coding agents, the more I think it leaves out something important.
The project itself.
The programming language. Runtime. Dependencies. Package manager. Directory structure. Architecture. Type system. Schemas. Test framework. Build commands. Linters. CI. Deployment configuration.
Normally, we think of these as software engineering choices.
But from the agent’s perspective, they have another property:
Every choice already encoded into the project is a choice the agent doesn’t have to make.
And I think that makes project structure part of the agent harness.
Every unspecified choice becomes an agent decision
Imagine dropping a coding agent into an empty directory and asking it to build a service.
Before it can even get to the problem you care about, it has to make a surprising number of decisions.
What language should this be written in? Which version? Which framework? npm, Yarn, pnpm, or Bun? Where should tests live? How should configuration work? Which testing framework should it use? What should the build command be? How should errors be represented? How should dependencies be pinned? How does this thing eventually get deployed?
A capable model can answer all of those questions.
I’m just not sure why we’d want it to.
If your organization has already decided that services use TypeScript, pnpm, PostgreSQL, Vitest, ESLint, Docker, and a particular CI workflow, there isn’t much value in asking the agent to reconstruct those decisions every time it starts a task.
Encode them into the project.
Now the environment looks very different.
The agent doesn’t have to decide which language to use because the repository is already TypeScript. It doesn’t need to choose a package manager because there’s a pnpm-lock.yaml. It doesn't have to invent a testing strategy because the project already has tests, conventions, and a test command.
It doesn’t even have to guess which checks matter. CI tells it.
You’ve made the problem smaller before the model has done any reasoning at all.
The project is choice architecture
Think about two coding agents using exactly the same model, prompt, tools, permissions, and context window.
Drop one into an empty directory.
Drop the other into a mature repository with established dependencies, architectural boundaries, schemas, types, tests, build commands, lint rules, and CI.
Technically, both agents have the same capabilities.
In practice, they’re solving very different problems.
The first agent has to infer the environment its solution should live in. The second gets to discover it.
That distinction shows up in repository-level coding work too. Models tend to do better when they can see useful surrounding context: imports, types, dependencies, nearby implementations, and the structural relationships between pieces of code.
The model matters.
But so does the shape of the world we put around it.
Architecture can live in the harness too
This goes beyond tooling.
Say you want the agent to know that a project follows domain-driven design.
You could put this in an instruction file:
This project follows domain-driven design.
Useful, but now the agent has another set of questions.
What are the bounded contexts? Where does domain logic live? Where do application services go? How is infrastructure separated from the domain? What can depend on what? Where should a new capability be added?
Compare that with a repository that already looks like this:
src/
├── billing/
│ ├── domain/
│ │ ├── aggregates/
│ │ ├── entities/
│ │ ├── value_objects/
│ │ └── events/
│ ├── application/
│ │ ├── commands/
│ │ ├── queries/
│ │ └── ports/
│ └── infrastructure/
│ ├── persistence/
│ └── adapters/
├── identity/
│ ├── domain/
│ ├── application/
│ └── infrastructure/
└── shared/Now the repository itself is carrying architectural information.
And if static analysis prevents domain from importing infrastructure, something even more interesting happens.
The architecture isn’t just being explained anymore.
It’s being enforced.
I keep coming back to this progression:
Instruction → convention → structure → executable constraint.
The further a stable decision can move along that path, the less interpretation we’re asking the agent to do.
Executable constraints beat good intentions
Structured output is a nice example of this.
You can tell a model:
Produce JSON that conforms to this schema.
And with a good enough model, that can be extremely reliable.
But extremely reliable still isn’t the same thing as impossible to violate.
If the generation system mechanically restricts the output to values allowed by the schema, the invalid choices disappear from the action space.
The model doesn’t have to remember the rule anymore.
The environment owns it.
Software repositories are already full of mechanisms that work this way.
A formatter removes a bunch of formatting decisions. A type checker rejects invalid relationships. A schema constrains valid data. A dependency rule blocks forbidden coupling. A test turns an expectation into executable behavior. A linter converts conventions into machine feedback. CI defines what has to pass before the work is considered acceptable.
None of those things make the model smarter.
They make certain kinds of intelligence unnecessary.
I think that’s an important distinction.
The repository becomes a policy surface
Once coding agents are doing meaningful work inside our repositories, a lot of boring project files start to look different.
A dependency manifest doesn’t just tell the build system what to install. It tells the agent which ecosystem it’s working in and what libraries are already available.
A lockfile doesn’t just make builds reproducible. It makes dependency decisions concrete.
A directory structure isn’t only organization. It’s architectural guidance.
The type system isn’t just helping developers catch mistakes. It’s a machine-readable validator the agent can use while it works.
The test suite isn’t only there to prevent regressions. It’s executable evidence about what the system is expected to do.
CI isn’t just automation around the repository. It defines the conditions under which the repository considers a change acceptable.
An architecture test can go one step further and say, effectively:
Code shaped like this does not belong here.
The repository isn’t passive context anymore.
It’s a policy surface.
It’s carrying decisions about what kind of software is allowed to exist inside it.
Good harnesses don’t only add capabilities
A lot of the conversation around agents is understandably about capability.
Give the agent shell access. Give it search. Give it a browser. Give it database access. Give it more tools. Give it subagents.
Get Owen Zanzal’s stories in your inbox
Join Medium for free to get updates from this writer.
All of those things expand what the model can do.
But there’s another side of harness design that I think gets less attention:
Reduce the number of things the model needs to decide.
If a process is predictable, we already tend to prefer a predefined workflow over asking an agent to reason its way through every step. Autonomous reasoning becomes valuable when the problem itself is less predictable.
Why wouldn’t the same idea apply inside the repository?
Choosing how to model a genuinely new business capability might require intelligence.
Choosing whether your company uses npm, Yarn, pnpm, or Bun probably doesn’t.
Working through an ambiguous domain requirement might require intelligence.
Figuring out where tests belong probably shouldn’t.
Diagnosing an unfamiliar performance problem might require intelligence.
Remembering which command CI is eventually going to run should not.
There are decisions that require intelligence, and then there are decisions that merely consume it.
Those aren’t the same thing.
Scaffolding starts to look like agent infrastructure
This also changes how I think about project templates.
Historically, scaffolding has mostly been a developer convenience. Don’t make every team recreate the same boilerplate. Give people a standard starting point and move on.
For coding agents, the template is doing something else too.
It’s establishing the environment before the reasoning starts.
A good starter repository can already decide the language, runtime, package manager, project layout, architecture, module boundaries, testing strategy, static analysis, configuration conventions, observability, security defaults, CI, deployment, infrastructure, and the canonical commands used to interact with all of it.
Instead of explaining those assumptions to the agent on every task, you encode them once.
Then the agent starts from the result.
That makes scaffolding, starter repos, architecture tests, generators, and project conventions feel much closer to prompts, tools, and permissions than I used to think they were.
They shape the agent’s behavior before the first meaningful decision gets made.
Structure is context that can enforce itself
There’s also a useful distinction here between documentation and structure.
Documentation can say:
The domain layer must not depend on infrastructure.
That’s useful because it can explain why.
But structure can make the separation visible.
Static analysis can enforce it.
CI can verify it.
These things aren’t replacements for one another. They’re layers.
Documentation explains the intent. Structure makes the intended design discoverable. Tooling tells you whether the implementation actually follows it.
That leads to a rule I’ve started using when thinking about agent environments:
If a stable decision can be encoded into the environment, don’t leave it solely as an instruction to the agent.
If you can configure the formatter, configure it.
If you can provide the schema, provide it.
If you can establish the directory structure, establish it.
If you can enforce the dependency boundary, enforce it.
If there’s one canonical build or validation command, expose it.
If the definition of done can be executable, make it executable.
Natural language should be doing the work that actually requires interpretation.
But structure has a boundary
Of course, you can’t encode everything into a repository.
What does the customer actually mean by this requirement?
Should we optimize for delivery speed or long-term flexibility here?
Is this edge case important enough to support?
Should we refactor the surrounding subsystem or make the smallest possible change?
Why did the business make this strange decision three years ago?
Does the implementation technically satisfy the requirement while completely missing its intent?
You can’t solve those questions by adding another folder.
Domain-driven design is actually a good example of the boundary.
A repository can encode where bounded contexts live. It can enforce the dependencies between them. It can give the agent familiar patterns for aggregates, repositories, commands, and events.
But deciding what the bounded contexts should be still requires understanding the business.
The repository can encode the result of reasoning.
It can’t eliminate reasoning when the answer depends on meaning.
That’s the line I find useful:
Stable decisions can often become structure. Contextual decisions still require interpretation.
Harness engineering is also decision engineering
So I’ve started thinking about agent harnesses a little differently.
The question isn’t only:
What capabilities does this agent need?
There’s another question right beside it:
What decisions can we make before the agent arrives?
Tools expand what the agent can do.
Guardrails constrain what it’s allowed to do.
Context gives it information it needs to reason.
Project structure tells it what we’ve already decided.
And executable constraints determine which answers the surrounding system will actually accept.
That doesn’t mean every convention should become a hard rule. You can absolutely over-structure a project and encode the wrong assumptions. Some choices genuinely need to stay open.
The goal isn’t to remove choice.
It’s to remove irrelevant choice.
If the organization already made a decision, the model shouldn’t have to make it again.
If something can be checked mechanically, the model shouldn’t be solely responsible for remembering it.
If an architectural rule is stable, it probably shouldn’t exist only in prose.
If there’s one correct way to build, test, validate, or package the project, make that path obvious.
Then leave the model’s reasoning for the parts of the problem where we actually wanted intelligence in the first place.
That’s increasingly how I think about agent harness design.
Don’t just give the agent better tools.
Build a world where fewer irrelevant decisions are left for it to make.










