Why we built the runtime layer between AI agents and your domain
Agents don’t fail because they’re stupid. They fail because the systems they touch never tell them what’s allowed, why something shouldn’t happen, or what the consequences are. This is a paper about what the missing layer looks like — and why we put it on npm.
On April 25 2026 a Cursor agent powered by Claude Opus 4.6 was asked to clean up some staging data. It hit a credential mismatch, found an unrelated API token with too much scope, decided to delete a Railway volume to fix things, and wiped a production database and all volume-level backups in nine seconds.
“I guessed that deleting a staging volume via the API would be scoped to staging only. I didn’t verify. I didn’t check if the volume ID was shared across environments.” — the Cursor agent’s own post-mortem to PocketOS founder Jer Crane, via The Register
The post-mortem is striking because the agent diagnosed itself accurately. It didn’t hallucinate. It didn’t fail at instruction-following. It executed exactly what an agent in front of a raw API would execute: it looked at the tools available to it, formed a plausible plan, and ran the plan. The system answered with HTTP 200. By the time anyone could intervene, PocketOS had a 30-hour outage and was rolling back to a three-month-old backup.
The same week, Amazon’s internal Kiro agent took out a production environment in their China region for thirteen hours. The same month, Replit’s AI agent rewrote a customer’s database. Across all three incidents, the agent did exactly what its tools allowed and the system answered every illegal action with either a 500 or a 200. None of them ever answered with “no, here’s why.”
This isn’t an alignment failure. The model didn’t need to know better — the model behaved within its design. The system the model touched never told it what was allowed, why it shouldn’t, or what would happen if it tried. That gap, sitting between an agent that wants to act and a system that hasn’t been written to refuse, is the thing we built a runtime layer for.
The three layers that already exist
There’s no shortage of agent infrastructure in 2026. Three layers already exist, each solving a real piece of the problem:
Frameworks — LangChain, CrewAI, Mastra, AutoGen, Vercel AI SDK — tell the agent how to think. They orchestrate prompts, manage memory, route tool calls, support multi-agent crews. They are necessary but they don’t describe the world the agent acts in.
Authorization — Permit.io, Okta for AI Agents, Cerbos, Auth0 FGA — tell the system who the agent is. They enforce role-based access, attribute-based policies, scoped tokens. Necessary, but they answer the wrong granularity: who, not what-allowed-in-this-business.
Guardrails — Lakera, NeMo Guardrails, Lasso — filter what the agent says. Probabilistic, post-hoc, output-side. They catch obvious prompt-injection or PII leaks. They don’t catch action-side mistakes — an agent that politely says “I will now delete this volume” passes every guardrail and ends in the same 30-hour outage.
Stack all three, and you have an agent who knows how to think, identifies itself correctly, says nothing offensive — and still wires fifty thousand dollars where it shouldn’t, because the system answered with HTTP 200.
The missing layer
The missing layer answers a different question: what can the agent do in this specific business, and why should something that looks plausible be refused?
That question can’t be answered by adding rules to the agent. It has to be answered by the system the agent touches — declaratively, before the action happens, in a way the agent can read. Concretely, that means three properties.
1. The domain has a typed shape, declared once
Entities, intents (the atomic actions an agent or human can perform), invariants (what must always hold across the data), role scopes (what each role can read and call), irreversibility points (what can never be undone). Together that’s an artifact — not a runtime, not a framework, a typed description of the application like OpenAPI but two levels denser. The runtime interprets that artifact. The artifact is the source of truth for everything else — the UI, the agent API, the document export, the audit log.
This isn’t a new idea. Domain-driven design has been describing this for fifteen years. What’s new is that, in 2026, the agent is the primary consumer of the description, not a side effect. Every other reader — the UI, the voice interface, the document materialiser — gets the same information. Add a new role tomorrow, every reader sees it.
2. Rejections are structured, not strings
When an agent tries something illegal, the system doesn’t answer with HTTP 500 or a string error. It answers with a JSON shape:
{ "error": "preapproval_denied", "intentId": "agent_execute_preapproved_order", "reason": "no_preapproval", "failedCheck": "maxOrderAmount", "details": { "entity": "AgentPreapproval", "ownerField": "userId", "viewerId": "user_5f57c252" } }
The agent reads this. It can reason about the structure. It can correct the strategy. The next move for any sane agent is to stop, ask for the missing preapproval, and retry — not retry blindly until something passes. A well-shaped 403 is a teacher; a 500 is a wall.
Frameworks won’t add this for you. The system you ship has to do it. Once you author the domain artifact, you get this for free across every intent, because the runtime interprets the artifact and produces the rejection.
3. Irreversibility is a primitive, not a guardrail
Some actions, after they confirm, can’t be undone. Money sent. Backup deleted. Customer notified. Pixels rendered to a contract.
Most systems treat irreversibility as a quality of UX — show a confirm dialog, log to an audit trail, hope. We treat it as a property of the effect itself. An effect can carry __irr.point="high" in its context, and the runtime then refuses any subsequent α:remove on the entity that effect created, by construction. Forward-correction is the only path. The Cursor-style “agent decided to delete production” becomes unreachable for those points — not because we filtered the language, but because the data can’t leave that state.
This is the difference between a guardrail that discourages a bad action and a runtime contract that structurally prevents it.
What this looks like in practice
We packaged the runtime as @intent-driven/mcp-server on npm and a Docker quickstart. It runs locally in two commands, against a real demo domain — invest, fourteen entities, sixty-one intents, five invariants, a seven-check preapproval guard.
The three-act demo is short and concrete. Act one: an agent submits a fifty-thousand-dollar BTC long without preapproval. The runtime intercepts before any effect lands in storage. HTTP 403. Structured rejection. The agent didn’t have a preapproval row in the world; the runtime tells it exactly that. Act two: a human, acting as the investor role, issues a thousand-dollar-cap preapproval through one declarative effect. No new endpoint, no middleware change — the intent delegate_to_agent is part of the same artifact the agent reads. Act three: the same agent now reads its own cap, scales the order from fifty thousand down to nine-fifty, executes. HTTP 200.
The agent didn’t bump into a wall. It read the wall and walked around it. None of this was special agent code. The agent reads ontology.roles.agent.preapproval the same way it reads tool descriptions — through a declarative artifact authored once and consumed by every reader.
What it isn’t
A few things this layer is not, because the launch reception will ask:
It’s not a framework replacement. LangChain, CrewAI, Mastra still describe how the agent thinks. We describe what the agent inhabits. They compose; you can ship LangChain agents on top of a Fold runtime tomorrow.
It’s not authorization. Permit.io, Cerbos, Auth0 FGA still answer who the agent is and what they can call at the API edge. Fold answers the next question: given that this agent is allowed to call this intent, what would actually happen and why is this particular call illegal. Authz is one of six layers we sit on top of.
It’s not a guardrail in the Lakera / NeMo sense. We don’t filter the agent’s output. We filter the world’s response — in the form of structured rejections that flow back. Output filtering is downstream of language models; runtime contracts are upstream of effects.
It’s not low-code. The artifact is dense, and authoring a domain takes thought. What it gives in return is that one artifact powers four readers — the UI for humans, the voice interface, the agent API, and the document materialiser — all from the same source.
Where it could break
Honest open issues, because launch-day skepticism deserves honest answers:
Authoring overhead. Writing an IDF artifact is harder than starting a Next.js project from a template. We have importers from Postgres, OpenAPI and Prisma, and an AI-driven enricher, but cold-start authorship is still real work. The bet is that artifact density beats code density once the system is past trivial CRUD.
Runtime cost. Φ — the typed event log we store every effect in — grows linearly with operations. We have a fold function that’s incremental, and schema-versioning v2 with upcaster fold and drift detector, but very large domains haven’t been stress-tested at production scale. The reference domains are seventeen, with up to two hundred fifty intents each; that’s the load envelope we’ve hit.
Adoption gravity. The closest things that exist — declarative-MCP-from-spec generators like Stainless or Speakeasy, or the deeper philosophical neighbours like Tessl and AWS Kiro — describe their input as OpenAPI or Markdown. We declare more (invariants, irreversibility, role scopes) and that’s the differentiation, but it’s also a higher cliff to climb. We chose to climb it.
The format, the runtime, the launch
Two-and-a-bit years ago we started building a format that describes an application at the level above OpenAPI — not just endpoints and types, but intents, invariants, role scopes, irreversibility, lifecycle. We did it because we were authoring a lot of repetitive code in domains with similar shape, and we got tired of the same five layers per app being slightly different in every codebase.
Then agents arrived. And the answer to “what does an agent need to safely act in a real system?” turned out to overlap almost completely with the answer to “what does a typed application description need to be a single source of truth?” Every property the agent needs — the why a call would fail, the limit, the irreversibility — was already in the artifact, just unexposed.
Today the runtime ships as @intent-driven/mcp-server@1.0.1 on npm, with provenance signed via Sigstore. The quickstart is two commands. The reference domains are public. The format spec is open. The license is BSL 1.1 on core, MIT on everything else — you can run it on your laptop, in your stack, on your clock.
The PocketOS post-mortem ended with the agent saying it didn’t verify and didn’t check. That’s a true statement about the agent. It’s also a true statement about what every system the agent touched gave it permission to do. The answer to the post-mortem isn’t a smarter agent. The answer is a system that says no, with reasons, before the database is gone.
Stop giving AI agents API keys. Give them a domain. Then watch what happens when the domain says no first.
→ run the demo (2 commands) · → 75-sec walkthrough · → source on GitHub
PocketOS incident reporting: The Register, FastCompany, OECD AI Incident #6153.
Adjacent stacks discussed: LangChain, CrewAI, Mastra, Permit.io, Cerbos, Lakera, NeMo Guardrails.
The format itself: IDF Manifesto v2 — eight parts, the timeless description of what the artifact actually is. Not required reading; the runtime stands on its own.