Why force the LLM to use files over vector database or key-value stores, just because it's a design principal for UNIX (which is designed for human users, not LLMs.)
Think of a typical loop we may ask of Claude Code today (assume we are not using TDD): run some test suite with fail fast mode, diagnose if the failure is due to recent feature changes (pass reference to backend/frontend, github issues, PRD,...). Ask CC to decide if test failed due to feature change and then update the test. Perhaps ask CC to use sub-agent to investigate and fix (if deemed so). Commit each fix, move on to next.
I know, this has so many ways to make blunder but I am talking about the agent here, not our error-prone test maintenance. What if we had an agent that had context of your codebase, deterministically ran test suite, linter, hooks, etc. The "English" prompt would become a code loop with the LLM only brought in to decide if a test has failed because of feature change. Also, we can extract git log, JIRA and what not.
Each tool here is real code. Executable code that calls others and only prompts when they meet edge cases. Edge cases are defined but we can now accelerate the maintenance of these tools using agents themselves. But the system is built on "programs that do one thing and do it well" and then reach out to an LLM for its specific edge case. The agent is how these executables work with each other.
My harness is a Claude Code plugin with its own brainstorming, adr, and planning skills with associated review and interview skills. Behavioral testing related to acceptance criteria is built in. Everything in my harness is gated to prevent ratholes.
I recently inflated a docker container to execute a set of work with Claude in unsafe mode and immediately saw problems with everything it was doing…and then I realized I had not installed my harness.
Running Claude without an engineering harness is like driving a car without brakes or a steering wheel.
But
I don't want the AI to summarize my email for me.
I don't want the AI to summarize my calendar for me.
I don't want the AI to summarize my Zoom call for me.
Thanks
Something I am convinced of though, there probably isn't a single `best` harness for all tasks. Different workloads will likely perform better with certain combinations of model + harness, especially when we are talking about token budgeting and cost tracking.
Ambiance feels like a great base “kernel” to build those variants on top of, rather than the one true harness.
> When in doubt, simplify. Remove, trim and minimize. Reproduce issues in as small cases as possible, understand the full design completely, there is no shortcuts for this.
One of the meta-processes designed in is pushing automated processes, both defined and discovered, down as far as possible. "Down" here means as far towards the metal as reasonable. So automate the automatable stuff, and leave the LLMs to do stuff LLMs are actually good at.
A trivial example is 'handle this bugfix ticket'. Many actions in a bugfix are pre-defined, for example a git commit at the end of the ticket. So Maelstrom will, at the end of a bugfix workflow, will force a git commit from the LLM that did the implementation. The LLM never even sees the git command, it just fills in a JSON field with a commit summary, and the workflow handles the commit.
There is this ACM blog post called "Manual Work is a Bug" [0] that was originally written to help humans automate processes using code. I find it just as applicable today as when it was written. You and the LLM look at what has to be done and then figure out the scripts/tools to make it happen. You then tie those tools into a system.
The more I use the above the more it makes sense and the worse the whole "just commit the prompt" seems like nonsense.
Gherkin style tests also come to mind
It parses the LLM output for tool calls, executes the command, and puts the output back into the LLM input. That's all there is to it.
There are some inroads into this vision - but I haven't seen anything build directly for this (beside my own experiment).
I have some 'vibe noted' notes on this: https://zby.github.io/commonplace/notes/unified-calling-conv..., https://zby.github.io/commonplace/notes/rlm-tendril-and-llm-...
OP's idea "everything is a text file" is good and I use it too. My plans are saved as task.md files, numbered and named. Work items are checkboxes inside the file, closed work items are checked and a comment is added on the same line to provide feedback about the implementation.
I also keep a current-state-of-the-world document, it should be <20KB of text, keep the essential decisions and intents. Loading it allows resuming in <30s.
Something I never saw anyone else do - I save all user messages in a chat_log.md file which is referenced for intent alignment and state recovery. I consider the chat log on the one hand, and coded tests on the other hand as the two walls, the agent works in the mid section between them.
https://horiacristescu.github.io/claude-playbook-plugin/docs...
What I am saying is the opposite - use Claude Code or whatever else - generate actual "programs". Basically scripts. We have tons of ways for "programs" to interact with each other. Then have clearly defined edge case handlers - think "try/catch". How far do you want to go down the rabbit hole in the "catch"? Do you want to re-write a new version of the "program" itself? I do not know, but this type of a system is what Unix already is, with the addition of programs themselves reaching out to LLMs in well defined edge case handlers.
If it had a lossless, massive context window (100m-1b tokens), then it will squash everything. Give it bash + r/w and it can in theory /goal anything.
I think there's something to be gained in a production environment be siloing agents for reproducebility/auditability, but I suspect that will go away in the future.
There's that video of a silly demo someone made of an OS that was just nested copilot instances that generated the HTML of each window, which allowed you to do whatever you could imagine. It was seen as silly because it was, but that seems truly transformative.
I feel like Docker Compose / K8S / VM / Dagger.io layers are close but can't quite always recurse flexibly and aren't always simple to run with. Networking / devices / auth are often awkward choke points
By trade I am a .Net software developer so as a lot of people would imagine — I was not able to accept a script that wouldn’t be reusable and flexible, basically over engineered.
I do quite some devops so I finally had to accept the fact that I can write simple script with hardcoded values that will live on a server (where I can copy paste and change values to meet other server) and most likely I will not have to look at that script for years as it will be running with cron doing its job without an issue.
Over engineered scripts designed from get go always required debugging from time to time so lots of time I was just doing stuff manually to make it quicker.
So I started winning when I accepted first script can be really simple and when needed I can move it to be parametrized but if not it will just keep doing it's job there on the server.
As coding agents have accelerated my work, I just build tons of tooling around existing software. Or in rare cases build new ones. If we zoom out of software engineering, we will still be in the realm of files - text or binary. That does not change.
The question is - do we let agents run the tools or the "programs" call the LLMs. The OS is the new agent, but not the same sense of "agent". I want LLMs to be lightly sprinkled in a future "agent" OS, not the other way around.
You may want to read earlier discussions https://news.ycombinator.com/item?id=48881112 And https://news.ycombinator.com/item?id=48051562
I
https://www.langchain.com/blog/tuning-the-harness-not-the-mo...
The API is basically what you see as a user of Claude Code or Pi or whatever. You can make new sessions, send messages to sessions, configure which MCPs get started, etc.
I’ve been poking at something similar to what you’re talking about via that route. My client prompts the agent to do a thing, and then afterwards launches deterministic things to check it which can either re-prompt the original session or start a new session.
Eg it automatically runs the tests afterwards, and will send a new prompt in the original chat to fix them if they fail. I also briefly poked at a security analyzer that gets changed files via git and makes a new session to check whether there are security issues and propose a fix that then gets sent to the original session.
If you want a circular loop where the LLM can adjust its own workflow while keeping it deterministic, you can let the agent modify the ACP client that drives it.
The upshot of this is you actually have a much better understanding of the different way your script needs to work if you're adapting it for a third use instance.
I've been thinking about how to free LLMs from the chat pane for a few years now. Watching what people have tried and seeing what worked (and didn't), I've formed my own opinions on the right way to approach it. Here are my thoughts, and what I've been working on recently.
With the advancing intelligence of LLMs, harnesses will eventually be reliable.
The real matter at hand is reducing how much cognitive load (measured in tokens) you are putting on your bot.
The most important things we've learned over the years.

A good harness MUST make use of the a priori coding knowledge of the LLM. Coding and systems administration are heavily overrepresented in the LLM's training data, so give it an environment it is already comfortable in; wrangling it through a novel one ultimately wastes tokens. Similarly, precious context should not be wasted on things like file discovery, traversal, etc. -- good harnesses make delegation easy and efficient. At the same time, a harness should feel light to the LLM, but actually do a lot of things in the background. These include logging, sanity checks, failsafes, sanitizations, etc.
Everything is vulnerable, and all agents eventually fail. There are two types of Agent failures:
Failures at the LLM level cannot be directly patched, but the risk of such a failure can be mitigated by the harness.
Harness-level failures can be recovered from, and should be fixable at runtime due to the turn-based nature of the LLM.
In order to fix a defect, the Agent needs two things: great logging, and a clear error message.
Most of these requirements are age-old questions; only the verbiage has shifted from Users to Agents. So it's worth asking: what can we learn from the before-fore times, when people used to actually write code?
I am, by no means, an expert on Unix or any of its descendants, but I have spent the last decade learning about its history, design choices, and usage. A lot of it maps beautifully onto our predicament; plenty of it doesn't. Think of U/L as a motivating analogy rather than a direct comparison.
In case you might have forgotten, or not seen it before, here is the creed of our forefathers Ritchie and Thompson:
- Write programs that do one thing and do it well. To do a new job, build afresh rather than complicate old programs by adding new "features".
- Write programs to work together. Expect the output of every program to be the input for another program.
- Write programs to handle text streams, because that is a universal interface.
These perfectly encapsulate the issues with harnesses today: they're overly complicated, attempting to do a lot of things, and the trajectories our Agents are expected to take are often ill-defined. These are symptoms of the same issue: there is no way for the Agent to hold agency over itself. In many cases today, tools are directly loaded into context, and system prompts are preemptively filled with warnings and specific rules and guidelines that developers have (which eventually fade turn-over-turn).
We can then derive our own set of principles for designing our harness:
- Write modular, transparent tools that do one thing and do it well; ensure they fail loudly.
- Write tools, skills, and connectors that work together. Skills dictate workflows, tools are the means to execute them, and connectors are the data which the Agent manipulates.
- Text streams are a universal interface, and a Language Model has home-court advantage. Everything should be a flat text file.
Have you ever manually done JSON wrangling? What about constructing heavy curl commands to query an endpoint? Complicated regex? I haven't, because they are a pain in the ass. An LLM might have an easier time with them than you, but rest assured that they prefer plaintext too. Thus, whenever dealing with external data sources, your harness should perform whatever manipulation might be necessary to clean it up before it reaches your LLM.
You need a place to store all this data. By categorizing everything into directories, you can save your Agent a lot of wasted tokens.
The FHS just outlines what a fresh Linux install should look like. Naturally, LLMs are experts at navigating the Linux FS, thus strategically planting our external data sources and routing into the VFS will allow the Agent to feel at home (e.g. your logs go into /var, configuration files go into /etc, Agent workspace at /home, etc.). An additional benefit is that both you and the Agent can easily audit, track, and search for things via grep, find, which, and even non-GNU programs such as rg and fzf.
The heavy lifting lies in making the messy, cluttered outside world fit into the faux-VFS.
I've been using the following mapping. Note that lots of things do directly translate 1:1.
| Harness | Unix Equivalent | FHS |
|---|---|---|
| Agents | Users | /home/... |
| External data | Drivers | /sys/ |
| Tools | Binaries | /bin/ |
| Logs | Logs | /var/ |
| Self-healing | System Binaries | /sbin/ & /recovery |
| Skills | Docs | /usr/share/doc |
Now that we've established what our filesystem should look like, let's think about how the Agent will interact with the environment (or rather, when). The industry standard for always-on Agents, OpenClaw, pairs event-driven message handling with a heartbeat: a full agent turn on a fixed interval (30 minutes by default) to check whether anything needs attention. The trouble is that everything that isn't a pushed message, like file changes or external state, only gets noticed at heartbeat granularity. Tighten the interval and you burn a full LLM turn on every empty check; loosen it and the agent is up to an hour behind the world.
Thus, I decided to build Ambiance around an event bus that I misnomerously call "the Kernel". It watches our FS for changes with cursors on textfiles, and then invokes the LLM accordingly (with a few coalescence strategies to handle high throughput). That way you can ensure that the Agent never misses a single notification. Additionally, you can also wire up different 'users' (LLM instances) to respond to different events.
A real kernel acts as the middle layer between software and hardware. The Ambiance Kernel acts as the middle layer between the LLM and the outside world. The Kernel checks and ensures everything the LLM does is safe and not obviously harmful.
There are three default users I've developed so far:
root, which handles all system-level stuff, especially coding new drivers, binaries and fixing old ones.pai, which is the human-facing LLM that actually interacts with the outside worldlibrarian, which journals what pai is good at, what it's bad at, and what the system did for the day.All three are constantly communicating with each other via the event bus and a send-message binary.
The idea behind Ambiance is simple: the model's priors are the cheapest resource you have, so a harness built out of things the model already knows (files, users, logs, docs) will always beat one it has to be taught. Everything else here is just in service of that.
Ambiance is a work in progress, but you can try it today at whitematterlabs.ai, or skip straight to:
curl -fsSL https://raw.githubusercontent.com/whitematterlabs/ambiance/main/install.sh | sh
PS: Do projects like this interest you? Are you working on projects like this yourself? You should totally check out the Recurse Center, which is where I started building this out!