We sell a two-week audit that ends in a record of what your agents did. So before writing another
line of copy about it, I wanted our own numbers — not a synthetic estate, ours. On 12 September I
wrapped the MCP server this studio actually uses, desktop-commander, in Witness, committed the
config, and went back to work.
Three days later I came back for the report. There was nothing to report. Not an empty log — no log
directory at all. ~/.witness/ did not exist.
What had happened
The wrapped entry in .mcp.json pointed at /opt/homebrew/opt/node@22/bin/node. That binary had
been pinned there since June, deliberately, because it was the stable install. Some time between
June and September, Homebrew moved simdutf to 9.1.0. Node 22.23.1 links against
libsimdutf.34.dylib. The loader went looking for it in six places, found it in none, and the
process died before it executed a line of JavaScript:
dyld[79301]: Library not loaded: /opt/homebrew/opt/simdutf/lib/libsimdutf.34.dylib
So the server never started. Witness, sitting in front of it, had nothing to relay and nothing to record. The harness reported a closed connection, which is exactly what a harness reports for a dozen more boring reasons, and I had not looked at that particular line in three days.
The uncomfortable part is that this is the failure mode our own documentation warns about, in a sentence I wrote: absence of evidence is never evidence of absence. An empty record is not a quiet estate. It is a question. I had built the product on that principle and then, on my own machine, read a missing log as nothing to see.
The actual session
I repointed the wrapper at the Node that runs — v26 — and drove a real working session through it: twenty-five read-only tool calls across this studio’s three repositories, the sort of thing an agent does for me on an ordinary afternoon. Reading page sources, listing component directories, checking a config, pulling file info.
That produced 54 records in one session, one server, one declared principal. The chain verified: 1 of 1, no broken links. Twenty-five tool calls, three errors, nothing unanswered.
Three things in it were not what I expected.
The aggregate error rate hid the only interesting number. Twelve percent across the session
looks unremarkable; you would not open a ticket for it. But all three failures were the same tool.
read_file was called twelve times and failed three — twenty-five percent — while every other tool
in the session failed zero times. The interesting figure was never the session average. It was one
tool, and the average is what buried it. Per-tool is the only breakdown worth printing.
The failures were an agent guessing. All three were reads of files that do not exist:
a src/content/config.ts in a project with no content collections, a tailwind.config.js in a
project that does not use Tailwind, a page that had not been written yet. Nothing malicious, nothing
broken. Just an agent reaching for the file it expected a project like this one to have. That is a
completely reasonable thing for an agent to do and a completely invisible one — it never reaches a
human, it never reaches a log anyone reads, and at scale it is indistinguishable from an agent
probing.
Latency lives in the tail. read_file ran a 62 ms median against a 529 ms ninety-fifth
percentile — eight and a half times the middle. list_directory sat at 2 ms flat across six calls.
The median told me the estate was fast. The p95 is the number that describes what waiting actually
feels like, and it only exists because something recorded every call rather than sampling them.
What we changed
The wrapper now points at a Node that runs. That is the small fix.
The larger one is that “no records” is now a state we treat as an alarm rather than a clean bill. A recorder that writes nothing and a recorder that is not running produce identical evidence, and the whole value of the thing is that it distinguishes them. Witness already prints its write failures on stderr and keeps relaying, which is the right behaviour — recording can be lost, uptime cannot — but stderr on a background MCP server is a place messages go to be unread. A recorder whose silence is ambiguous is doing half a job.
I would rather find this on my own machine than in week one of someone else’s audit. It is also, straightforwardly, one of the six things a Flight Check looks for: the config on the machine differs from the config in the repo, and nobody notices until someone asks the record a question.
Twenty-five calls is not ten days. The recorder is running properly now, and when there is a real multi-week record of this studio’s own agents, I will publish those numbers too — including the ones that are boring.