
Every failure I have had building with language models eventually reduced to one question: what did the model see, and what happened next. Four disciplines have grown up around that question. They get used interchangeably, and they should not be — each one sits a layer above the last, and a fix aimed at the wrong layer does nothing.
The narrowest layer, and the one everyone learns first. You have a single request and a single response, and you are shaping the words in between. Say what role the model is playing, give it an example, tell it what to do when it does not know.
Prompt engineering stops being enough the moment the interesting part is not how you asked but what was in front of the model when you asked.
A context window is a budget, and everything competing for it is in tension: instructions, retrieved documents, conversation history, tool output, the file you are editing. Context engineering is deciding what earns its place.
The failure here is quieter than a bad prompt. Nothing errors. The model simply answers from the wrong half of what you gave it, or from a stale copy of a file, and the answer reads perfectly well.
The harness is everything around the model call. Which tools exist, what they return, what happens when one fails, what the model is permitted to do without asking. A harness is where an assistant becomes an agent.
Most of the work is in the boring parts: error messages a model can act on, tools that fail loudly instead of returning empty, and permissions that are refusals rather than suggestions.
// A tool that fails loudly. Swallowing this error into a default value
// would turn a service outage into a page that renders empty and looks
// fine -- the failure mode you can't see is the expensive one.
try {
return await client.fetch<T>(query, params, { next: { tags, revalidate } })
} catch (error) {
console.error('[sanity] query failed', { tags, error })
throw error
}The outermost layer, and the one that decides whether an agent finishes. A loop runs: the model acts, something changes, the model sees the result and acts again. Loop engineering is the design of that cycle — when it stops, what it remembers, how it recovers, and how a person steps in.
The questions that matter at this layer:
A loop that cannot tell it has failed will keep going. That is not a model problem; it is a design problem, and it is solved by giving the loop something real to check against.

| Layer | You control | Typical symptom |
|---|---|---|
| Prompt | Wording of one request | Right information, wrong shape of answer |
| Context | What is in the window | Confident answer drawn from the wrong material |
| Harness | Tools, errors, permissions | Model tries the right thing and cannot do it |
| Loop | Iteration, stopping, recovery | Work never finishes, or finishes wrong and says it is done |
The practical value of separating them is diagnostic. If an agent keeps producing plausible nonsense, rewording the prompt is the wrong move — check what it was actually given. If it keeps trying a tool that does not exist, that is the harness. If it declares success on work it has not done, that is the loop, and no amount of prompting fixes it.
Anthropic's Claude Code documentation is the clearest worked example of a harness and a loop I know of that you can actually read end to end.
If you are building something that needs an agent to finish work reliably rather than demo well, I am available for new projects.
Tell me about your project