DEV Community

Cover image for The Two Layers That Decide Whether Your Agent Survives
Shashi Kanth
Shashi Kanth

Posted on • Originally published at allsrc.dev

The Two Layers That Decide Whether Your Agent Survives

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                 │
               └─────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

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:

  1. 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.

  2. Fail closed. An unregistered tool, a missing policy, an unknown token, an
    exhausted budget: all resolve to denial. Registration is not authorization.

  3. 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.

  4. 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.

  5. 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


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)

Collapse
 
mads_hansen_27b33ebfee4c9 profile image
Mads Hansen

The DENY > PAUSE > ALLOW rule 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 typed indeterminate outcome—after a timeout, retry logic must reconcile the provider receipt before deciding whether another attempt is safe.