ActaClad Research · 01

Boundaries,
not breaches.

What scanning thirteen widely used open-source AI projects taught us about production AI

ResearchAI Production Readiness

Conversations about AI risk almost always start in the same place. Prompt injection. Jailbreaks. Data exfiltration. Model theft. The vocabulary is borrowed from security, and so is the mental picture: someone outside, trying to get in.

We went looking somewhere duller.

We pointed our static analyzer at thirteen widely used open-source AI projects — AutoGen, LangGraph, CrewAI, LlamaIndex, Dify, AutoGPT, browser-use and others, more than 1.08 million GitHub stars between them — and asked a question that sounds almost naive:

What, exactly, bounds this system's behaviour?

We did not find one spectacular vulnerability. We found a pattern, and it repeated across projects built by different teams, in different years, for different purposes.

These systems could routinely do more than anyone had explicitly permitted them to do. Not because someone had broken in. Because nobody had drawn the line.

The shape changed. The controls didn't.

A conventional application has a shape you can hold in your head. A request arrives, your code runs, a response goes out. Every arrow in that diagram is one you drew, and the amount of work that happens is a decision you made at authoring time.

An agentic application does not work like that.

CONVENTIONAL request function response you decide how much work happens AGENTIC request model tool retrieval response …and the model influences how many times to go round
The loop is the whole difference. In the second diagram, the amount of work is influenced at runtime by a probability distribution.

Every discipline that has met this problem before answered it the same way: with explicit ceilings. Memory limits. Connection pool sizes. Query timeouts. Rate limits. Nobody considers those advanced safety engineering — they are the ordinary furniture of a production system.

Those ceilings have not yet been reapplied to a system that decides for itself how much work to do. Here is what that looked like across thirteen codebases.

Finding 01

Output with no clear limit

10 production findings across 7 of 13 projects

A model call with no cap on how much it may generate. Not one of the frameworks sets a default, and neither do the provider SDKs underneath them — so the omission travels down a dependency chain nobody owns end to end, and arrives in an application whose author never made a decision about it at all.

File this under cost and you have misread it. Uncapped generation is a spend risk, a latency risk and an availability risk simultaneously — and the security community has already named the failure mode. Unbounded consumption sits in the OWASP Top 10 for LLM applications, carrying a subcategory with the bluntest name in the field: denial of wallet. An attacker who cannot steal your data may settle for making you pay for their compute.

The finance side of the house reached the same place from the opposite direction. In two years, managing AI spend went from a specialism to simply the job:

202420252026 31%63%98% Share of FinOps practitioners managing AI spend · State of FinOps 2026, n=1,192
Finding 02

Agents with no clear stopping point

Agent teams constructed with no turn cap and no termination condition

In AutoGen we found team objects built with neither a maximum number of turns nor a termination condition. It matters that we say this precisely: that is not a bug in Microsoft's code. It is documented behaviour. The framework has no default turn cap to fall back on. If the developer does not supply a bound, there isn't one.

Which is a design decision with an entirely predictable consequence. When the safe configuration takes an extra argument and the unsafe one takes nothing, the corpus fills up with the unsafe one.

Think about what we would never do with a person. You do not hand a new joiner a company card with no limit, no approval threshold and no expiry, tell them to be helpful, and walk away. Nobody would call that trust. They would call it an absence of process.

That is close to the default posture for an autonomous agent today. Here is the panel that ought to exist — and what it reads on arrival:

The Agent Execution Budget
Turnstimes round the loop— not set
Durationwall-clock ceiling— not set
Tokenstotal generation budget— not set
Tool callshow many actions, of what kind— not set
Retriesbefore it stops trying— not set
Depthhow far recursion may go— not set
Spendthe number, in currency— not set

Seven dials, and on a default installation every one of them reads blank. We think this panel deserves a name and a place in the design review, the same way a connection pool does.

Finding 03

Everything ends up in the logs

15 production findings across 7 of 13 projects

This was the most common finding in the study: personal data or raw model output written straight into a log sink.

A traditional application logs what it did — a receipt. An agentic application, left alone, logs what it thought. That is a diary, not a receipt, and it lands in a store that almost always has weaker access controls than the database the data came from.

prompt completion retrieved documents tool arguments tool results & agent state logger.info() ordinary application logs weaker access controls longer retention wider audience
An agentic system produces far more sensitive intermediate data than a conventional one — and the default path for all of it is the same log file.

The instructive part is that the standards body worked this out first. OpenTelemetry's conventions for generative AI capture no prompt content or tool arguments by default, precisely because they may be sensitive. Content capture is opt-in, and when enabled the content is carried in a form a collector can strip centrally without touching application code.

Observability has stopped being only a debugging layer. It is now a data-governance boundary.
Finding 04

AI dependencies expire

A model retired in July 2025, still named in a current example

In LangGraph's command-line example graphs sits a model identifier that was retired thirteen months ago:

model_name="claude-3-sonnet-20240229"

Nothing catches this. The tests mock the provider, so they pass. The type checker is content. There is no warning and no gradual degradation — the call simply works until the morning it doesn't. A model identifier is the only dependency in software that arrives with a scheduled failure date.

It is a recipe card left in a shop window with an ingredient discontinued last year. The card looks fine. Everyone who follows it discovers the problem in their own kitchen.

Conventional dependency management assumes a version stays available until you upgrade it. AI applications now depend on model versions, providers, hosted APIs and tool servers, and any of those can be withdrawn on a published schedule that has nothing to do with your release calendar. That is a genuinely new category of maintenance, and almost nobody is tracking it.

Finding 05

Example code is part of the supply chain

The patterns above cluster in quickstarts, samples and templates

Notice where that retired model lives. Not in LangGraph's internals — in its examples.

The instinct is to care less about that. We think the opposite is closer to true.

framework quickstart example copied verbatim customised production no upstream fix ever reaches the right-hand side

Example code is the highest-leverage code in an open-source project, because it is copied verbatim by people who reasonably read it as the maintainers' recommendation. A defect in a framework's internals reaches the users who hit that path, and an upstream release fixes it. A defect in a quickstart reaches everyone who starts there — and then it lives in their repository, where no upstream fix will ever find it.

The AI supply chain begins before pip install.

This gets harder, quite soon

Almost nothing we found was an attack. It was production engineering debt — missing output bounds, missing termination conditions, model identifiers with expiry dates, sensitive content taking the default path. Ordinary controls, not yet reapplied to a system that decides for itself how much work to do.

And the surface is expanding faster than manual discipline can follow. Gartner's guidance on managing agent sprawl puts building a centralised agent inventory as the second of six steps, and projects that the average Fortune 500 will run over 150,000 agents by 2028 — up from fewer than fifteen in 2025.

Whatever you do by hand across fifteen things does not survive contact with that number.

If you take one thing from this

Pick one agent your team runs in production and answer four questions out loud.

What caps its output? What stops the loop? Where does its reasoning get written down? What happens the day the model it names is retired?

If any answer is "I'd have to check" — that is the finding. It is also, in our experience, the most common one.

What we're building, and why

This study is the reason ActaClad exists in the shape it does. Three questions have to be answerable before an enterprise can trust an AI system in production, and they are answered at different moments in its life.

KnowWhat AI components exist, who owns them, and what they are permitted to do. You cannot bound an estate you cannot enumerate.
AssureWhether the code was built with bounds in the first place — before it ships. That is Plumbline, the open-source analyzer that ran this study.
ObserveWhat the system actually does once it is live, so the bounds that were declared can be checked against the behaviour that occurred.

Plumbline is the part you can use today. It reads your code and tells you what has no bounds before it ships — 32 rules across 12 families, against a published 61-rule taxonomy. Open source under Apache-2.0, offline by default, and every rule is readable, because a finding you cannot argue with is a finding you will eventually ignore.

The question is no longer only "is this model safe?" It is "what bounds this system — and how would we know?"

Method

Plumbline was run against thirteen public repositories, Python source only. Star counts and file contents were verified against each project's live default branch on the date of writing — every code snippet quoted here was copied from the live file, not from our report.

Findings we could not verify by hand are not in this article, and neither are per-project scores or any ranking of one project against another. Comparing thirteen codebases of different sizes, ages and purposes on a single number would produce a league table we could not defend, so we have not built one.

Sources. OWASP Top 10 for LLM Applications (unbounded consumption, excessive agency); State of FinOps 2026, FinOps Foundation (n=1,192); OpenTelemetry semantic conventions for generative AI; Gartner, Six Steps to Manage AI Agent Sprawl (April 2026). Nothing here is cited from a source we have not read.

Our thanks to the maintainers of all thirteen projects. Building in the open means the decisions are visible — and across this study, most of what we found was people getting it right in a discipline that is three years old.