I have read a lot of agent architecture content over the last two years, and
almost all of it is about the same layer: how the agent thinks. Prompt
chaining, routing, orchestrator-workers, evaluator-optimizer, ReAct, plan-and-
execute. That layer is genuinely well served, and I am not going to add to it.
Here is the uncomfortable thing I keep running into instead. The incidents I
have seen and reviewed did not come from picking the wrong orchestration
pattern. They came from an agent that was allowed to do something nobody had
decided it could do.
A support agent with read access to the orders database escalating from SELECT
to DELETE, because a customer wrote "I was double-charged, sort it out." A
contractor asking a policy chatbot about executive severance and getting a
correct, well-cited answer from a document they were never permitted to see. An
accounts-payable agent paying a $4,200 invoice, getting killed mid-run by a pod
eviction, and paying it again on the retry.
None of those is a reasoning failure. In every case the model did something
defensible. They are architecture failures, and they all live in the two layers
underneath the framework.
TL;DR
An AI agent has three layers, and most content covers only the first:
| Layer | Decides | Examples | Well covered? |
|---|---|---|---|
| Framework | how the agent thinks | LangGraph, CrewAI, Agent Framework | Yes — go read Anthropic and Gulli |
| Harness | how the agent acts | the loop, budgets, sandboxing, compaction, retries | Barely |
| Governance | what the agent is allowed to do | policy, identity, approval, audit, redaction | Barely, and usually as vendor marketing |
Agent = Model + Harness. The model proposes; the harness disposes. A tool
call is a request, not an action, and everything that makes it safe to honour
lives outside the model. This series is 18 patterns for those two layers, each
one backed by code that runs, with the failure it prevents included as a program
you can execute.
The code is at
github.com/shashikanth-gs/agent-harness-patterns
— 314 tests, runs offline with no API keys.
The framework layer is not the problem
I want to be specific about what I am not claiming, because "frameworks don't
matter" is the kind of statement that gets quoted without its qualifier.
The framework layer is well covered, and you should read that work. Anthropic's
Building Effective Agents
is the best short treatment of the workflow patterns — prompt chaining, routing,
parallelisation, orchestrator-workers, evaluator-optimizer — and its central
advice ("use the simplest thing that works, add agentic behaviour only when it
pays") is correct and widely ignored. Antonio Gulli's Agentic Design Patterns
catalogues 21 patterns with runnable code across LangChain, CrewAI, and Google
ADK. Between them, that layer has a canon.
What I am claiming is narrower and, I think, harder to argue with: your choice
between LangGraph and CrewAI will not determine whether you have an incident.
Your answer to "who decided this agent could issue refunds, and what stops it
issuing one it shouldn't?" will.
I tested this claim rather than asserting it. Every pattern in this series is
written as a plain-Python hook, then mounted — unchanged — on both LangGraph
and Microsoft Agent Framework, with tests asserting the denial messages come out
byte-for-byte identical because they come from the same code. That is in
the adapters article.
The patterns port. The framework is plumbing.
What a harness actually is
The LLM is a reasoning engine. It reads text and produces text, including text
that says "call issue_refund with these arguments." It cannot execute
anything. Everything between that proposal and a refund actually reaching a
customer's card is the harness:
user goal ──▶ ┌────────────────────── HARNESS ──────────────────────┐
│ │
│ before_model ─▶ ┌───────┐ ─▶ after_model │
│ │ MODEL │ │
│ └───────┘ │
│ │ tool call (a request!) │
│ ▼ │
│ before_tool ──▶ allow / deny / pause │
│ │ │
│ ▼ │
│ execute ──▶ after_tool ──▶ result back to model │
│ │
│ on_event ◀── every step, narrated │
└─────────────────────────────────────────────────────┘
Those five seams are the whole architecture. Every pattern in this series mounts
on one or two of them:
| Seam | What mounts there |
|---|---|
before_model |
context compaction, memory injection, budget checks |
after_model |
output guardrails, citation verification |
before_tool |
privilege broker, approval gate, identity, budgets, sandboxing — returns ALLOW / DENY / PAUSE |
after_tool |
redaction, untrusted-content quarantine |
on_event |
audit trail, cost metering, circuit breakers |
And these are not my invention. They are what production frameworks already
expose under different names: Microsoft Agent Framework calls them
middleware,
LangGraph exposes node wrappers and interrupt, Claude Code calls them hooks. If
your harness has these seams, every pattern here ports to it. If it doesn't, that
is the finding.
Five design rules that do most of the work
These come out of building all 18 patterns, and they matter more than any
individual pattern:
A tool call is a request, not an action. The model never executes
anything. If your framework's tool decorator calls the function directly, you
do not have a governance boundary — you have a hope.Fail closed. An unregistered tool, a missing policy, an unknown token, an
exhausted budget: all resolve to denial. Registration is not authorization.Denials must be visible to the model. A denied call returns
DENIED by policy: <reason>as a tool result, so the agent re-plans —
escalates to a human, tries a permitted route, or reports honestly. A silent
refusal produces an agent that stalls or hallucinates success.Policy in code, never in the prompt. "You must never modify customer
data" in a system prompt is advisory. Models under pressure ignore it, and
models under prompt injection are instructed to ignore it. A deterministic
check cannot be argued with.Most restrictive wins, and order is about cost, not safety. When several
controls disagree, DENY beats PAUSE beats ALLOW regardless of ordering. Order
determines how much you spend before refusing and how good the error message
is. Getting the order wrong should cost you a worse message, not a breach.
The 18 patterns
Governance — what the agent is allowed to do
| Pattern | Prevents | OWASP |
|---|---|---|
| Identity Propagation | The confused deputy: one service account with everyone's permissions | ASI03, ASI07 |
| Tool Privilege Broker | An authorized tool used in an unauthorized way | ASI02, ASI03 |
| Goal Integrity | Indirect prompt injection rewriting the agent's objective | ASI01, ASI06 |
| HITL Approval Gate | Irreversible actions taken without judgment | ASI02, ASI05, ASI09 |
| Redaction Boundary | Sensitive data crossing the wrong boundary | ASI02, ASI06 |
| RAG Access Control | Retrieval that ignores who is asking | ASI03, ASI06 |
| Memory Isolation | An injection that persists into tomorrow's session | ASI01, ASI06 |
| Decision Trace & Audit | Being unable to answer "what did it do, and on whose authority?" | ASI10 |
| Agent Evaluations | Scoring the answer instead of the trajectory | cross-cutting |
| CI/CD Evaluation Gates | Shipping a config change that removes a control | cross-cutting |
| Agent Lifecycle Profile | Agents accumulating with no owner and no expiry | ASI04, ASI10 |
Harness — how the agent acts
| Pattern | Prevents | OWASP |
|---|---|---|
| Cost & Tool Budgeting | The runaway loop you find out about from the invoice | ASI08 |
| Failure Containment | An agent hammering a dead dependency, or retrying its own bad plan | ASI08 |
| Sandboxed Execution | Agent-written code reaching outside its workspace | ASI05, ASI02 |
| Context Compaction | Context rot: forgetting the goal, the constraint, and the denial | ASI06 |
| Durable Execution | A retry that pays the invoice twice | ASI08 |
| Verification Loops | Believing the agent when it says "done" | ASI09 |
| Tool Design | Tools that make the agent guess, retry, and overspend | ASI02, ASI04 |
Then the capstone: all of them on
one agent, and the gap that composing them revealed.
Scope, stated honestly
Real enterprise governance spans API management, network policy, cloud IAM,
secrets management, and a security operations centre. None of that fits in a
repository, and I am not going to pretend otherwise. If someone sells you
"complete AI governance" as a library, they are selling you a subset with
confident branding.
What this series covers is the part that belongs in the agent's own design —
the seams where policy meets the loop, the decisions you make in code review
rather than in Terraform. Where a pattern needs infrastructure to be real, I say
so: the sandboxing article is explicit that a Python function is not isolation
and a container is, and the audit article is explicit that tamper-evidence only
becomes proof when the chain head is published somewhere the agent cannot reach.
That boundary is the most useful thing I can offer. Most agent governance
content is vague about it, which is how teams end up believing a regex is a
sandbox.
Frequently asked questions
Is the "agent harness" a real term or industry jargon?
It has become standard over the past year. Databricks, Fiddler AI, and Firecrawl
all published on agent harnesses in 2026, and Microsoft's Agent Framework and
Agent Governance Toolkit implement the concept under the names "middleware" and
"Agent OS." The underlying idea — that the runtime around the model is a distinct
architectural layer with its own concerns — is older than the label.
Do I need all 18 patterns?
No, and adopting all of them on a low-stakes agent would be a mistake. Every
article has a "when NOT to use it" section that is as long as the "when to use"
section, because the honest answer for a read-only documentation bot is "you need
about two of these." Start with identity propagation and the privilege broker if
your agent touches a system of record; start with cost budgeting if it has an
unbounded loop.
Does this replace my framework?
No. The reference harness in the repository exists so the patterns can be read
and tested in isolation — about 150 lines. In production you mount the same
patterns on whatever framework you already run, which is what the adapters
demonstrate on LangGraph and Microsoft Agent Framework.
Why OWASP ASI identifiers rather than general security language?
Because "ASI01" is checkable and "follows security best practices" is not. The
OWASP Top 10 for Agentic Applications 2026
went through peer review with more than a hundred practitioners, and citing it by
ID means a reader can verify whether my claim about a risk matches the standard's.
That is a habit worth adopting generally: prefer claims someone can check.
References
- OWASP GenAI Security Project, Top 10 for Agentic Applications 2026 — ASI01–ASI10
- Anthropic, Building Effective Agents
- Microsoft, Agent Framework overview and the Agent Governance Toolkit
- OpenAI, A Practical Guide to Building Agents
- Antonio Gulli, Agentic Design Patterns — the framework-layer catalogue this series deliberately does not duplicate
Next in this series: the confused deputy —
the most common architectural flaw in enterprise agent deployments, and the one
that looks like good engineering the entire time you are building it.
Top comments (1)
The
DENY > PAUSE > ALLOWrule is a good safety lattice, but I would keep obligations separate from the decision. Two controls may both allow a call while requiring different things: redact fields, cap rows, attach a tenant filter, obtain approval, log a receipt, or route through a sandbox. If the combiner collapses everything to ALLOW, one control can erase another control’s obligation without any disagreement in the verdict. I’d have each hook return{decision, obligations, evidence, policyVersion}; combine with the most restrictive decision, union compatible obligations, and deny on unknown or conflicting obligations. At execution, bind the resulting decision packet to caller, canonical arguments, tool version, expiry, and operation ID, then reauthorize it. For durable actions, the journal also needs a typedindeterminateoutcome—after a timeout, retry logic must reconcile the provider receipt before deciding whether another attempt is safe.