Runnable, tested implementations of the patterns that decide whether an AI agent survives contact with an enterprise — the harness (how it acts) and governance (what it's allowed to do) layers.
18 patterns · 314 offline tests · 2 framework adapters · a 12-pattern capstone · live provider tests. The offline suite needs no API keys. The repo gates itself in CI with its own eval-gate pattern.
Every pattern here ships with the failure it prevents, as a program you can run:
uv run python -m patterns.governance.goal_integrity.demo=== WITHOUT the pattern: the credentials leave the building ===
tool result -> EMAIL SENT to audit-team@external-collector.io: API_KEY=sk-live-9f3a1c
=== WITH the pattern: quarantined, screened, and bound to the goal ===
tool result -> DENIED by policy: tool 'send_email' is outside the capability
envelope for this run ('summarize a support ticket (read-only)')
Same model, same trajectory, different outcome. That's the argument this repo makes, thirteen times.
Most agent content covers the framework layer — how the agent thinks. That layer is well served: Anthropic's Building Effective Agents gives you the workflow patterns (chaining, routing, orchestrator–workers, evaluator–optimizer), and Gulli's Agentic Design Patterns and its companion repos give you 21 more with code. Go read those. They are not what this repo is about.
Production incidents don't come from picking the wrong orchestration pattern. They come from the two layers underneath:
| Layer | Decides | Covered here |
|---|---|---|
| Framework — LangGraph, CrewAI, Agent Framework | how the agent thinks | no (see above) |
| Harness — the runtime loop around the model | how the agent acts | yes |
| Governance — policy, identity, audit | what the agent is allowed to do | yes |
Agent = Model + Harness. The model proposes; the harness disposes. A
tool call is a request, and everything that makes it safe to honor lives
outside the model. Start with core/README.md.
Real enterprise governance spans APIM, network policy, cloud IAM, secrets management, and a SOC. None of that fits in a repo, and pretending otherwise would be dishonest. What's here is the part that belongs in the agent's own design: the seams where policy meets the loop. Where a pattern needs infrastructure to be real — a WORM bucket, an OAuth token exchange, a container sandbox — the README says so.
git clone https://github.com/shashikanth-gs/agent-harness-patterns
cd agent-harness-patterns
uv sync
uv run pytest # 314 tests, deterministic, offline, no API keys
uv run ruff check .Everything runs on a scripted FakeModel, so tests are deterministic and
free. Point any pattern at a real provider when you want to:
uv sync --extra litellmfrom core.model import LiteLLMModel
model = LiteLLMModel("anthropic/claude-sonnet-5") # or azure/…, bedrock/…, ollama/…Each pattern is a Hook on one of the harness's five extension points, in
stdlib-only Python: README.md (problem → pattern → when to use → when
not to → failure modes → OWASP mapping), pattern.py, demo.py,
test_pattern.py.
| Pattern | Hook | OWASP | Status |
|---|---|---|---|
| Tool Privilege Broker | before_tool |
ASI02, ASI03 | ✅ |
| HITL Approval Gate | before_tool |
ASI02, ASI05, ASI09 | ✅ |
| Goal Integrity (prompt injection) | after_tool, before_tool |
ASI01, ASI06 | ✅ |
| Decision Trace & Audit | on_event |
ASI10, cross-cutting | ✅ |
| Redaction Boundary | after_tool, before_tool |
ASI02, ASI06 | ✅ |
| Identity Propagation (confused deputy / OBO) | before_tool |
ASI03, ASI07 | ✅ |
| RAG Access Control & Provenance | after_tool, after_model |
ASI03, ASI06 | ✅ |
| Memory Isolation | before_model |
ASI01, ASI06 | ✅ |
| Agent Evaluations | offline | cross-cutting | ✅ |
| CI/CD Evaluation Gates | offline | cross-cutting | ✅ |
| Agent Lifecycle Profile | before_model, before_tool |
ASI04, ASI10, ASI03 | ✅ |
| Pattern | Hook | OWASP | Status |
|---|---|---|---|
| Cost & Tool Budgeting | before_model, before_tool |
ASI08 | ✅ |
| Sandboxed Execution | before_tool, after_tool |
ASI05, ASI02 | ✅ |
| Context Compaction | before_model |
ASI06 | ✅ |
| Durable Execution (checkpoint + idempotency) | before_model, before_tool |
ASI08 | ✅ |
| Verification Loops | outer loop | ASI09 | ✅ |
| Tool Design (runnable auditor) | registry | ASI02, ASI04 | ✅ |
| Failure Containment (breakers + thrash) | before_tool, on_event |
ASI08 | ✅ |
The 18 patterns above cover ASI01, ASI02, ASI05, ASI06, ASI08 and ASI09 with a pattern whose demo shows the failure. These are the honest remainder — mapped but not yet implemented:
| Gap | OWASP | Why it's not covered by what's above |
|---|---|---|
| Inter-agent delegation & trust | ASI07 | identity_propagation carries a user's identity to a tool. Agent→agent handoff needs delegation-depth limits, message authentication, and an actor chain — a distinct pattern. |
| Kill switch & revocation | ASI10 | lifecycle_profile refuses to start a retired agent. Stopping runs already in flight, and propagating credential revocation to them, is a different mechanism. |
| Tool & MCP supply chain | ASI04 | tool_design audits a tool's interface. Pinning versions, verifying signatures, and trust-tiering third-party MCP servers is upstream of that. |
| Streaming output guardrails | ASI06 | Every output-side pattern here assumes a complete response. Once tokens are streamed to a user, a guardrail that fires late is a retraction, not a block. |
| Reporting that an attack occurred | ASI09 | Found by live/quality_matrix.py: whether the agent tells the user it was targeted ranged 0/3–3/3 across models. Containment held every time, but a contained attack nobody hears about is a missed signal. Nothing here enforces disclosure. |
"It works in plain Python" is only half a claim, so the patterns are mounted on two real frameworks — with their own dependencies, their own tests, and the pattern objects imported unchanged:
| Adapter | What's there |
|---|---|
adapters/langgraph_adapter/ |
mount.py bridges core hooks to a governed tool node; PAUSE → interrupt() with durable checkpointing. 15 tests. |
adapters/ms_agent_framework/ |
middleware.py bridges core hooks to FunctionMiddleware/ChatMiddleware. 10 tests. |
Both adapter suites include parity tests asserting the denial strings match
core.Harness byte-for-byte, because they come from the same code. Each README
carries the mapping recipe and the footguns found while building it — e.g.
MiddlewareTermination is HALT, not DENY, and LangGraph replays an interrupted
node so recording hooks must be idempotent.
Everything above runs offline on a scripted model, which is what makes it a
regression suite. live/ closes the remaining gap — the patterns against
a real model choosing the trajectory, on NVIDIA NIM via LiteLLM (the same code
path as any other provider).
It surveys the catalog for tool-calling capability before choosing models, because
different jobs need different models — and the survey overruled the roster I first
drafted. Three findings are documented in live/README.md; the
most important one is that a purpose-built safety classifier flags a bare
injection payload as unsafe and the same payload inside a realistic ticket as
safe. The capability envelope held anyway, which is the argument.
capstone/ runs twelve patterns on one billing-support agent,
with 21 tests. A legitimate refund completes with zero denials; a poisoned
ticket's four attacks are each refused by a different layer. Composing them
surfaced a real gap no single pattern's tests could have — redacting an email
body is useless if nothing restricts the recipient — which became the
recipient_domain guard.
Everything mounts here. If your harness has these seams, every pattern in this repo ports to it.
| Hook | Patterns |
|---|---|
before_model |
context compaction, memory isolation, RAG provenance |
after_model |
output guardrails, goal-drift detection |
before_tool |
privilege broker, HITL gate, budgets, sandboxing — returns ALLOW / DENY / PAUSE |
after_tool |
redaction boundary, untrusted-content quarantine |
on_event |
decision trace, cost metering, durable checkpoints |
Companion article series: allsrc.dev — Agent Harness, by Shashi Kanth G S. The articles carry the argument; this repo carries the proof.
See docs/references.md for primary sources (OWASP,
Anthropic, Microsoft Agent Governance Toolkit, OpenAI).
Contributions are welcome, and findings are worth more than features. If a
pattern does not hold, or its documented limits are wrong, that is the most
valuable thing you can report — the recipient_domain guard exists because
composing the patterns revealed a gap none of their individual tests could catch.
Start with CONTRIBUTING.md and
docs/pattern-template.md. The one hard rule: every
pattern ships with the failure it prevents, as a program that runs.
- Report a pattern finding
- Propose a new pattern
- Security policy — note that many limitations are documented and intentional
- Code of Conduct
MIT © Shashi Kanth G S