DEV Community

Cover image for Harness Engineering - Part 6: The Filesystem & Environment
Fikayo Adepoju
Fikayo Adepoju

Posted on

Harness Engineering - Part 6: The Filesystem & Environment

Welcome back to the Harness Engineering series — a 10-part journey from raw language model to production-ready agentic system. Made by builders. For builders.

In Part 5, we looked at the Context — the payload the model sees on every call. Now we look at what happens after the model, having seen that Context, decides to do something.

The model calls a tool. The tool has to execute somewhere. That somewhere is the Environment.

It's easy to under-appreciate. The Environment feels like plumbing — the filesystem, the shell, the network, the machine underneath. But it's where every side effect the model requests actually lands, and how you design it is what separates "an AI agent doing things on your behalf" from scary to routine.

What's ahead:

  1. Part 1: The Raw Model Problem
  2. Part 2: Defining the Harness — The Six Components
  3. Part 3: The Control Loop
  4. Part 4: The Tool Layer
  5. Part 5: Context Engineering
  6. The Filesystem & Environment ← You are here
  7. Part 7: The Memory Layer
  8. Part 8: Observability
  9. Part 9: The Harness Architecture
  10. Part 10: Decomposing Claude Code

By the end of this article, you'll know what the Environment actually is, why every tool with side effects depends on it, and the three properties (bounded, reproducible, inspectable) that separate a production-ready environment from a demo one.

Let's get started.


📚 Want to go deeper than the articles?

While you follow along with this series, I've put together two hands-on resources that go further than any single article can:

Both are optional — the series stands on its own. But if you want the full studio-quality version, that's where it lives.


What The Environment Is

The Environment is the runtime that tools operate inside. Concretely, it includes:

  • The filesystem they read from and write to
  • The shell they exec commands in
  • The network they can reach
  • The compute resources they're permitted to consume — CPU, memory, disk, wall-clock time, API quotas

If a tool has any kind of side effect, the Environment is where that side effect materializes. When your read_file tool opens a file, it's opening a file in an environment. When your bash tool runs ls, it's running that in some shell, on some filesystem. When your fetch_url tool fires an HTTP request, it's doing so from some network stack, subject to some rules about what it can reach.

None of this is exotic. If you've ever set up a CI pipeline, you've made environment decisions — what OS the runner uses, what dependencies are pre-installed, what secrets are exposed, what artifacts persist between steps. The Environment for an agent is the same kind of concern, applied to the same kind of question: inside what world does this thing run?

Why The Environment Exists

Because tools can't exist in a vacuum.

A read_file tool is meaningless without a filesystem to read from. A bash tool is meaningless without a shell to run in. A fetch_url tool is meaningless without a network to reach out through. Every tool with side effects needs a target for those side effects, and the Environment is that target.

Put another way: the Tools (Part 4) are the model's reach, but the Environment is what they reach into. You can't separate the two. A well-designed tool set embedded in a poorly-designed environment produces an agent that either can't act (because the environment blocks it) or acts too freely (because the environment doesn't).

What a Good Environment Design Looks Like

Three properties separate a production-ready Environment from a demo one.

Bounded

The Environment defines the scope within which the agent can operate. Good environments are bounded — the agent can do the things it needs to do, and nothing else.

That means:

  • The agent can't accidentally rm -rf the host machine
  • The agent can't reach across into networks it shouldn't touch — production databases, internal services, and so on
  • The agent can't quietly exfiltrate data through unexpected channels
  • The agent can't burn through cloud credits by spinning up resources unchecked
  • The agent can't stay running forever if it gets stuck

Boundedness isn't about not trusting the model. It's about containing blast radius. Even a perfectly-behaved model, working on a legitimate task, will occasionally make mistakes — hallucinate a filename, misread an argument, run a command with unintended flags. In an unbounded environment, one of those mistakes can wreck real things. In a bounded environment, the same mistake gets caught by the walls.

Reproducible

Every run starts from a clean, identical state. The Environment is recreated cleanly per task, not reused across tasks.

This one takes a moment to appreciate. In a reproducible environment, the agent doesn't inherit anything from previous work — no leftover files, no mutated database rows, no dirty processes, no stale caches. Every task begins from the same known baseline.

Why does this matter? Two reasons:

  • When something goes wrong, you can re-run it and see the same failure. That means the failure is the agent's, not the environment's. Debugging becomes possible. Without reproducibility, "it worked yesterday" becomes an unanswerable question, and you spend hours chasing environmental drift instead of actual bugs.
  • Runs don't contaminate each other. An agent that made a mess in Task A doesn't ship that mess into Task B.

Reproducibility is what makes agentic behavior testable. Without it, every run is bespoke, and the whole system stops being an engineering artifact and starts being an unpredictable pet.

Inspectable

Engineers need to be able to see what the agent did to the Environment after a run.

  • What files changed?
  • What commands ran?
  • What processes were spawned?
  • What network calls were made?
  • What resources were consumed?

Inspectability is the Environment's contribution to observability (which we look at in Part 8). It's not enough to know what the model said; you need to see what actually happened in the world as a result. Those two things do not always agree.

The model can claim it "successfully updated the config" while the config file, on inspection, hasn't been touched. Or it can claim to have "cleaned up temporary files" while /tmp is stuffed with leftovers. An inspectable environment gives you ground truth to check the model's story against.

Example: Codex

Codex is a clean example of what a well-designed Environment enables.

When you hand Codex a task, it runs inside a sandboxed container that gets a fresh clone of your repository. Inside that container, the agent can do essentially anything — read files, edit them, run tests, spin up services, execute arbitrary code, hit the network. And critically, it does all of that without ever touching your real machine.

When the task is done, the container is destroyed. The agent's changes come back to you as a pull request you can review before anything real changes.

That environment is why engineers trust Codex to run code they didn't write.

Notice the trade the sandbox enables. Inside the sandbox, the agent has a lot of freedom — it can delete files, run wild bash, hit external services. The freedom is what makes it useful. But the blast radius is bounded to a disposable container. Whatever damage the agent could theoretically do stays inside walls that get torn down at the end of the task.

Without the sandbox, the same agent doing the same work would be touching your real filesystem, your real credentials, your real repositories. The tools would be identical. The model would be identical. But the risk profile would be completely different.

The sandbox is what turns "let an AI run arbitrary code" from a reckless idea into a routine engineering practice. That's the Environment doing the load-bearing work.

Where This Leaves Us

The Environment is the runtime that everything else takes place inside. The Loop drives the cycle. The Tools define what the model can request. The Context defines what the model sees when it decides. And the Environment defines where the doing happens and what the doing is allowed to touch.

Together, those four components already give you an agent that can act on the world within bounds. But there's still something missing — something you feel the absence of the moment a session ends or the context window fills up. The agent forgets. Everything it learned, discovered, or decided during a task disappears.

That's what Part 7 is about: the Memory Layer. How to give the harness persistence — within a task, and across sessions.


Remember that this article is part of a longer 10-part series that walks you through every component of an agentic harness.

Here's the roadmap:

  1. Part 1: The Raw Model Problem
  2. Part 2: Defining the Harness — The Six Components
  3. Part 3: The Control Loop
  4. Part 4: The Tool Layer
  5. Part 5: Context Engineering
  6. The Filesystem & Environment ← You just finished this one.
  7. Part 7: The Memory LayerMove to this one.
  8. Part 8: Observability
  9. Part 9: The Harness Architecture
  10. Part 10: Decomposing Claude Code

See you in the next one.

Happy coding :)

Top comments (0)