Module 06
Why Models Fail
On this page
In plain words
A new teammate can be brilliant and still wreck your project if nobody gives him a task sheet, rules, tests and a handover note. AI agents fail the same way. The model is fine; the workplace around it is missing. Build seven surfaces around it, and the same model suddenly becomes reliable.
How it flows
- 1Give instructions
- 2Save state
- 3Fix scope
- 4Read real output
- 5Verify with tests
- 6Review and hand off
A tiny example
task = load_state("agent_state.json")
for step in range(limit):
move = think(task)
if not allowed(move, scope):
continue
result = run_tool(move)
task = save_state(task, move, result)
if tests_pass():
write_handoff(task)Notice the loop closes on the state file and on real tool output, never on the agent's own claim of success.
What you will learn
- Why a very smart model still produces work you cannot ship.
- The seven surfaces that turn a model into a reliable worker.
- How to see those seven surfaces as ordinary backend building blocks you already know.
- How to spot which surface is missing when an agent misbehaves.
The problem, simply
Think about your final-year project. Four people, one repo, submission next week. A new teammate joins on Monday. He is genuinely brilliant, coding is no problem for him. But nobody tells him which module is his, nobody shows him the review comments, nobody says what "done" means.
So he opens random files, changes a bit here and there, says "done bhai", and goes home. On Tuesday he comes back and remembers nothing. You run the code. Two things that were working yesterday are now broken.
Was he a bad engineer? No. He had no workplace. No task sheet, no rules, no test to run, nobody checking his work, no handover note.
Same thing happens with an AI agent. You drop a top model into a real repo and say "add input validation". It touches four files, writes code that looks perfect, says success, and stops. You run the tests. Two fail. One file it edited had nothing to do with validation. And there is no record of what it assumed or what is left.
The model was not wrong about Python. It was wrong about the work. That is not a model bug. That is a workbench bug.
The idea
What a workbench is
A workbench is the working environment you build around the model. Think of the workshop table in your college lab: tools in fixed places, a job card, a gauge to measure with, a supervisor who signs off. The person is skilled. The table is what makes the work repeatable.
An agent workbench has seven surfaces:
| Surface | What it carries | What breaks without it |
|---|---|---|
| Instructions | Startup rules, forbidden actions, what "done" means | Agent guesses what shipping means |
| State | Current task, files touched, blockers, next action | Every session restarts from zero |
| Scope | Files it may touch, files it may not, acceptance list | Edits leak into unrelated code |
| Feedback | The real output of the commands it ran | Agent celebrates while the server returns an error |
| Verification | Tests, lint, a smoke run, a scope check | "Looks good" reaches main branch |
| Review | A second pass by a different role | Builder marks its own homework |
| Handoff | What changed, why, what is pending | Next session rediscovers everything |
- 1Task
- 2Scope contract
- 3Repo memory
- 4Agent loop
- 5Real output
- 6Verification
- 7Review
- 8Handoff
Notice where the loop closes. It closes on a state file inside the repo, not on chat history. Chat gets cleared, context windows fill up, sessions die. The repo survives.
IMPImportant: The workbench is separate from the model. You can swap the model and keep the surfaces. You cannot remove the surfaces and keep the reliability.
Workbench is not prompting, and not a framework
Prompting tells the model what you want in this one turn. A workbench tells it how to work across many turns and many sessions. Most "the agent failed" stories are workbench failures dressed up as prompting problems.
A framework like LangGraph (a library for wiring agent steps into a graph) gives you a runtime. A workbench gives the agent a place to work inside that runtime. You need both.
Underneath, it is just backend engineering
Here is the trick that will help you in interviews. Remove the word "agent". An agent run is just computation spread across time, processes and machines. To make that reliable you need the same eight things any production backend needs.
| Building block | For an agent it is |
|---|---|
| Function | A tool call, a rule check, a verification step |
| Worker | The builder, the reviewer, the verifier |
| Trigger | A loop tick, an HTTP request, a cron, a file change |
| Runtime | The process that decides what runs where, with what timeout |
| Request wire | The tool-call protocol, the model API call |
| Queue | The task board, the feedback log, the review inbox |
| Session persistence | A state file in the repo, checkpoints, a key-value store |
| Authorization policy | Allowed and forbidden files, approval boundaries |
Now map the seven surfaces onto these. Instructions are policy. State is session persistence. Scope is an access-control list. Feedback is a durable log. Verification is a function that fails closed. Review is a separate worker with read-only access to the builder's output. Handoff is a record written by a session-end trigger.
Remember: When someone sells you new "harness" vocabulary, translate it back to these eight things first. Hooks are triggers. Memory is persistence. Subagents are workers. Guardrails are policy plus verification. The words change every six months; the engineering does not.
A worked example
Suppose Priya asks her agent: "Add ₹ amount validation to payment.py and add a test."
Prompt-only run. The agent edits payment.py, then also edits billing.py because it looked related, then says done. Tests were never run. The pull request breaks the billing flow. Two surfaces were missing: scope let it wander, verification let it ship.
Workbench run. Scope says only payment.py and tests/ may be touched. The agent tries billing.py and is blocked. Then verification actually runs the tests, sees one failure, and refuses to close the task. The agent fixes it and writes a handoff line: "validation added, negative-amount case pending". Same model, same task, very different outcome.
- 1Agent edits billing.py
- 2Scope blocks it
- 3Tests run for real
- 4One test fails
- 5Task stays open
- 6Fix and handoff
The numbers back this up. One company deleted about eighty percent of its agent's tools and success went from eighty percent to a hundred. In one terminal-task benchmark, the same model jumped from outside the top thirty to around rank five purely by changing the harness around it, not the model.
Build it
"""Two runs of the same toy agent: bare, then on a workbench."""
ALL_SURFACES = ["instructions", "state", "scope", "feedback",
"verification", "review", "handoff"]
SYMPTOM = {
"instructions": "agent guessed what 'done' means",
"state": "next session will start from zero",
"scope": "edited a file nobody asked for",
"feedback": "believed its own success message",
"verification": "broken work called finished",
"review": "builder marked its own homework",
"handoff": "no note left for the next session",
}
ALLOWED = {"payment.py"} # the scope contract for this task
def toy_model(step):
"""A fake model. Same three moves every time. No API call."""
return ["edit payment.py", "edit billing.py", "claim done"][step]
def fake_tests(touched):
"""Pretend test run. Passes only if just payment.py changed."""
return touched == ["payment.py"]
def run(name, surfaces):
touched, log = [], []
for step in range(3):
move = toy_model(step)
if move.startswith("edit"):
filename = move.split()[1]
# scope surface: block writes outside the allowed set
if "scope" in surfaces and filename not in ALLOWED:
log.append("blocked " + filename + " (outside scope)")
continue
touched.append(filename)
log.append("edited " + filename)
else:
# feedback surface: read the real result instead of the claim
if "feedback" in surfaces:
log.append("agent claims done, real tests say "
+ str(fake_tests(touched)))
else:
log.append("agent claims done, nobody checked")
# verification surface: the gate that fails closed
shipped = fake_tests(touched) if "verification" in surfaces else True
print("\n--- " + name + " ---")
for line in log:
print(" " + line)
print(" files touched:", touched)
print(" shipped:", shipped)
for surface in ALL_SURFACES:
if surface not in surfaces:
print(" missing " + surface.ljust(13) + "-> " + SYMPTOM[surface])
run("prompt only", set())
run("workbench", set(ALL_SURFACES))Run it with python3 file.py. Look at three things. First, the prompt-only run touches billing.py and still reports shipped as True. Second, the workbench run blocks that file and ships only because the tests really passed. Third, the missing-surface lines are a small failure-mode report: each missing surface maps to one symptom you can go and fix.
Where you will see this
- Claude Code, Codex and Cursor: the project instructions file is the instructions surface, slash commands set scope, hooks act as verification.
- LangGraph and similar runtimes: checkpoints and session stores are the state surface, handoffs between agents are the handoff surface.
- A normal CI pipeline on any repo: tests and lint are verification, the pull-request template is handoff, code owners are review.
- Customer-support bots: the allowed-actions list (can issue refund up to ₹500, cannot close an account) is pure scope and policy.
- Swiggy or Flipkart style assistants: the order state in the database, not the chat, is the system of record.
Common mistakes
- Treating chat history as memory. When the context window fills or the session dies, everything is gone. Keep the task state in a file inside the repo.
- Letting the agent report its own success. If you do not capture the real command output, the agent will happily declare victory on top of a failing test.
- Giving the agent every tool you own. More tools means more wrong turns; trimming the tool list is one of the cheapest reliability wins.
- No scope contract. Without an allowed-files list, small tasks quietly touch unrelated code and your review becomes a nightmare.
- Waiting for a smarter model to fix a workbench problem. A better model with no verification gate just writes wrong code more confidently.
If they ask in an interview
Q: Your agent works in a demo but fails on a real repo. How do you debug it?
A: I would not start with the model. I would check the seven surfaces one by one: are instructions explicit, is there a state file, is scope defined, is real command output fed back, is there a verification gate, a review step, and a handoff record. The missing surface usually maps directly to the symptom you are seeing.
Q: What is the difference between prompt engineering and workbench engineering?
A: Prompting shapes one turn. A workbench shapes work across turns and sessions: state that survives restarts, scope that limits writes, verification that fails closed. Prompting alone cannot make a multi-session task resumable.
Q: Why do you say a harness is just distributed-systems work?
A: Because every piece maps to something older: functions, workers, triggers, a runtime, a request wire, queues, session persistence and authorization policy. Hooks are triggers, memory is persistence, subagents are workers. Once you see that, you can evaluate any new agent framework quickly.
Try these
- Take a repo where you already use an AI assistant. Score each of the seven surfaces from 0 (missing) to 2 (healthy). Write down your weakest one and one concrete fix for it.
- Change the toy model in the code so it claims done at step one, before editing anything. Check which surface catches it first.
- Add an eighth surface of your own to the script. Then try to argue honestly that it does not collapse into one of the existing seven.
- Write a ten-line instructions file for one of your own projects: what done means, which files are off limits, which command must pass before anything is called finished.
Words, simply
| Word | Meaning in simple words |
|---|---|
| Workbench | The setup around the model: rules, state, scope, tests, review |
| Surface | One named thing the agent reads or writes every turn |
| Harness | The machinery around the model that actually runs the work |
| System of record | The file everyone trusts as truth when chat is gone |
| Definition of done | An objective checklist the agent cannot fake |
| Scope contract | The list of files and actions the agent is allowed to touch |
| Fails closed | If the check cannot pass, nothing ships |
| Handoff | The note the next session reads to continue your work |
Quick recap
- Capable models still fail because the work environment around them is missing, not because they cannot code.
- Seven surfaces make an agent reliable: instructions, state, scope, feedback, verification, review, handoff.
- Underneath, all of it is ordinary backend engineering, so translate every new buzzword back to functions, workers, triggers, runtimes, queues, persistence and policy.