What a Harness Can Promise That a Prompt Can't

Specialized work calls for specialized tools. “Early Surgical Tools” by Double—M, CC BY 2.0.
A short follow-up to What Is a Harness?, prompted by a question on Bluesky.
On Bluesky, my friend N Sims asked a good question about AI agents: how much can you trust the logs? If an agent says “I read the source and it supports the claim,” is that true, or is it just more generated text?
My short answer: it depends almost entirely on the harness. Explaining why this is true is a good practical example of how different harnesses can lead to different results. If you are “just” prompting a standard chat tool, you get very few hard guarantees. The model’s account of what it did is written by the same model that did it. But a harness can move some of those promises out of the model and into ordinary, boring, deterministic code. Here is what that looks like for one task I have been experimenting with: checking whether a Wikipedia citation supports the sentence it is attached to.
#The naive version
The naive approach to citation checking is a prompt to a chat tool:
Read the source at this URL. Does it support the claim “X”?
You will get an answer. It will usually sound confident. But because a chat tool is by nature a general purpose harness, it doesn’t create any of the guarantees you would want for citation checking, like:
- that the model fetched the URL at all;
- that the fetch worked, instead of returning a 404 page, a paywall, or a cookie banner;
- that the answer came from the page, rather than from what the model remembers about the topic;
- that the “I read the source” in its reply describes something that happened.
Most modern chat harnesses will attempt to fetch pages, and often they’ll get it right. The problem is that you can’t tell, from the outside, which answers are right. The instruction “read this source” is a request, not a guarantee.
#Harness step one: fetch before the model
Someone building a harness optimized for cite-checking, in contrast, could ensure that the fetching is guaranteed. The harness, using traditional deterministic software, would:
- Fetch the URL.
- If the fetch fails (the page doesn’t exist, returns a 404, or is empty), stop. Record “source unavailable.” The model never sees this citation.
- If the fetch succeeds, extract the text and hand that text to the model, along with the claim.
This is dull code. That’s the point. Now “the source was fetched, accessed, and handed to the model” is a fact the harness can log, because the harness did all that, in a loggable and trustworthy way. The log entry is written by the program, not narrated by the model. And the most embarrassing failure—a confident “yes, supported” about a page that doesn’t exist—can’t happen, because a page that doesn’t exist never reaches the model.
In the first post’s terms, fetching is integration, handing the model only the fetched text is context management, and stopping on a 404 is error recovery.
#Harness step two: make the model show its work, then check it
On the output side, don’t make the naive ask to the LLM of “does source X support claim Y?” Ask instead, to the combination of deterministic code and non-deterministic LLM:
Does source X support claim Y? If yes, give the exact passage from X that supports it.
Then, again in ordinary code, search the fetched text for that passage. If the passage isn’t there, the harness doesn’t report “supported.” It reports a failure, or asks the model to try again, depending on how you want to structure the work. In the first post’s terms, this is ground-truth validation, with error recovery when the check fails.
A model can still misjudge whether a real passage supports a claim. But it can no longer invent a supporting quote and have that quote shown to a human reviewer as if it were real. The human reviewer also gets something useful: a highlighted passage to check, rather than a verdict to take on faith.
#This is a lot more work
Building a harness that does all this subtle, persistent, deterministic checking is a lot more work than the naive approach, unfortunately. Some examples from trying it:
- Models paraphrase their own “exact” quotes. They fix typos, change curly quotes to straight ones, and drop words in the middle. So the “search for the string” step has to tolerate formatting noise (whitespace, dashes, capitalization, ”…”) without tolerating actual rewording. Drawing that line is careful, fiddly work.
- Fetching is its own hard problem. A page that loads fine in your browser can come back empty to a script, or come back without the table that holds the key fact.
None of this is visible if you only type into a chat box. That is also the point: the naive approach looks cheaper because it hides these failures instead of catching them.
#Back to the question
So: can you trust an agent’s logs—in other words, can you get what the first post called observability? For the parts the harness does in code—fetching, checking that a quote exists, recording what happened—yes, about as much as you trust any traditional software’s logs. For the parts the model does—judging whether a passage really supports a claim—no more than you trust the model. A good harness is honest about which is which.
That split is what I meant in the first post when I said permissions and guardrails should “live in deterministic code (and outside the inner loop).” It’s also why I think the most important question about LLMs and Wikipedia is not “can a chatbot check citations?” but “which parts of citation-checking can we take away from the chatbot entirely?”