Agent in Production

YAML Schema Design for Claude Code Agents

Governance rules for AI agents need to live in code, not assumptions.

Editor at Large · · 13 min read
Cover illustration for “YAML Schema Design for Claude Code Agents”
Agent-as-Code Patterns · September 5, 2026 · 13 min read · 3,011 words

Before 2025, an "agent" mostly meant a model running on someone's laptop, taking prompts inside an IDE, with no infrastructure around it beyond a terminal window. That world is ending. Engineering teams are now delegating entire tasks (database migrations, incident triage, full code reviews, multi-file refactors) instead of asking a model to finish a line of code, and Gartner's projection captures the pace of the shift: fewer than 5% of enterprise applications featured task-specific AI agents in 2025, with 40% projected by 2026. That gap closes fast, and closing it means agents will be touching production code, secrets, CI pipelines, and customer data at scale, mostly without a human watching each individual action.

This creates a governance problem that didn't exist when agents were personal tools. A developer running an agent on a laptop is a prototype. A team running agents across dozens of repositories, sharing credentials, with no record of what ran or why, is a liability waiting to surface on an incident report. If agents are becoming infrastructure, then the files that configure them need to be treated with the same discipline as infrastructure code, which raises a specific question: what does that configuration actually need to encode?

Why YAML config files are the natural home for agent governance decisions

Agents-as-code is the operating principle here, and it borrows directly from infrastructure-as-code's core insight: define the thing in a file, check that file into version control, and every change to it becomes visible, reviewable, and reversible. A permission decision made in a YAML field shows up in a pull request diff. A budget cap set in config gets enforced before a session starts, not discovered three weeks later in a billing dashboard. A tool restriction written into the file can't be talked around by a clever prompt, because it isn't the model enforcing it; it's the platform.

The alternative is what most teams already have: a scattering of shell flags, environment variables, and undocumented wrapper scripts that nobody can fully reconstruct six months later. Ask someone why an agent has write access to a particular directory and the honest answer is often "someone set that up during a hackathon and it stuck."

Research on how experienced developers actually work with these tools backs this up. A 2025 study combining field observations with a 99-respondent survey found that skilled engineers don't hand agents full autonomy; they exercise careful control and active supervision, checking output at defined points rather than letting a session run unattended. YAML config is what that control looks like once a team scales past one person. It's the structural form supervision takes when supervision can't be a person sitting there watching every token.

Claude Code agents live as Markdown files with YAML frontmatter, stored in .claude/agents/, with team-shared agents living in .claude/agents/team/. That structure isn't arbitrary. The frontmatter encodes what the agent can do; the Markdown body encodes what it should do. Permissions versus behavior. It's a clean split, and it maps onto the argument this piece makes: every field in that frontmatter block is a governance decision wearing the costume of a configuration setting. The rest of this piece works through those fields one at a time.

How name and description define the agent's identity and delegation boundary

The name field is a unique slug, used for invocation and routing. It also shows up in every audit log entry the agent generates, which means naming conventions aren't a style preference; they're a forensic decision made months before anyone needs the forensics.

description matters more. It's the single field an orchestrator reads to decide which agent gets a delegated task, written in plain language rather than structured metadata. A vague description ("handles code stuff") produces ambiguous delegation, where two agents could plausibly claim the same task and neither is clearly right. A description that's too broad means an agent starts receiving work well outside what it was built for, quietly, without anyone deciding that should happen.

Precision in this field functions as scope control. Naming exactly what an agent handles implicitly excludes everything else, which is exactly the mechanism that stops an orchestrator from routing a production deployment task to an agent built to review pull requests. Nobody has to write that exclusion rule; it falls out of the description doing its job.

Consistent slug conventions, something like security-auditor or db-migrator rather than agent7 or helper-final, make logs readable months after the fact. Opaque names create dead ends during incident review, right when readability matters most. These two fields read like documentation on the surface. Functionally, they're identity, and identity is the precondition for everything that follows: what an agent is allowed to touch.

Tool allowlists as the primary mechanism for scoping what an agent can do

The tools field is an allowlist, plain and simple: name the tools an agent can invoke, and everything not named is off by default. This is the single most consequential governance lever in the schema, because restricting tools doesn't just limit what an agent can do; it redefines what role the agent occupies. An agent with [Read, Grep, Glob] and nothing else is structurally a reviewer. It cannot introduce a bug through a write operation or exfiltrate a customer record through a network call, no matter how its system prompt is phrased or how it's coaxed by a malicious input, because the write and network tools were never granted in the first place.

That last point matters more than it might look. Tool restriction is enforced at the platform layer, not the prompt layer, so it doesn't depend on the model reliably following an instruction. A security auditor agent gets read-only tools. A database expert agent might reasonably need [Read, Write, Grep, Bash], but pairs that broader grant with explicit prohibitions against destructive operations baked into its system prompt as a second, belt-and-suspenders layer. A PR reviewer agent gets no Bash and no Write; it can analyze a diff, but it cannot execute anything or touch a file.

There's also a "restricted mode" flag worth mentioning, which constrains file-system tools to the working directory alone. That's a sandbox within the sandbox, useful precisely because it limits blast radius without requiring anyone to enumerate every path that should be off-limits.

None of this is hypothetical risk management. The OWASP Agentic Top 10 for 2026 lists tool misuse and privilege abuse among its ten highest-priority agentic risks, and tool allowlists are the schema-level answer to both. The design principle follows naturally: start from the smallest toolset that lets an agent do its stated job, and expand only when a specific, justified need shows up. Expanding later, deliberately, beats granting broadly and hoping nothing goes wrong.

Model selection as a cost and capability governance decision

The model field, sonnet versus opus, for instance, decides which model variant an agent runs on, and that choice has direct cost and latency consequences on every single invocation, not just the expensive ones. Assigning a heavier, more capable model to a lightweight, repetitive task, running a top-tier model to reformat docstrings, say, isn't a technical mistake. It's a budget decision, made by default because nobody thought to make it on purpose.

Per-agent model assignment is how cost tiering works across a system with several agents running at once. High-stakes work that happens rarely (an architecture review, a security audit) can justify a more capable model, because the invocation count stays low. High-frequency, routine work (generating test cases, drafting a PR description) should run on something faster and cheaper, because it happens dozens of times a day and the cost compounds.

That decision belongs in the YAML file, not in whatever a developer happens to type at the moment they kick off a session. Once it's in config, it's consistent across every invocation, visible to a reviewer, and changeable through a pull request rather than a Slack message asking someone to remember to switch models next time.

The stakes here are larger than they look at the level of a single agent. Enterprise spending on LLM APIs more than doubled within a single year, reaching a multibillion-dollar figure by mid-2025, and Anthropic holds the largest share of that spend among frontier providers. At that scale, per-agent model governance stops being a minor optimization and becomes a real line item. Model selection sets the cost of a single invocation. The next layer of control is capping what a whole session can spend before it runs away.

Session budget caps and why they must be set before the session starts

Session budgets are a YAML-level field in the Managed Agents platform schema, and the design has one deliberately harsh feature: the cap is set at session creation, and removal is one-way. A team can never add a budget cap after a session is already running.

That one-way constraint is the point, not a limitation to work around. It closes off the pattern where a team plans to "add controls later" and never does, because later never comes when there's no forcing function. By making the cap something that has to exist before the session starts or not at all, the schema turns a governance decision that would otherwise get deferred indefinitely into one that has to happen now.

What happens without that cap is predictable: a long-context loop, a recursive task that keeps calling itself, a large codebase analysis that spirals past its expected scope, any of these can generate costs that dwarf the value of the work the agent was asked to do. Per-session caps set at the schema level are a structural answer to runaway cost, catching the problem before it happens rather than explaining it after the invoice arrives.

Budget governance works best across a few distinct scopes. Per session, set in the agent's own YAML, caps the worst-case cost of one run. Per developer or team is enforced at the platform layer, since it isn't something a single agent file can express. Per time period means rate limits that stop automation from running unattended overnight or across a weekend, when nobody's watching the dashboard. Treat a missing budget field the way a security review would treat a missing authentication field: not an oversight to patch later, but a violation to catch in code review, on the first pass.

Credential scoping and why agent secrets should not live in the same config as agent behavior

Bundling credentials into the same file that defines agent behavior creates a specific, well-understood set of risks: secrets checked into a repository, credentials shared across sessions that have no business sharing them, and no way to revoke access cleanly once a session ends.

CVE-2026-21852 shows what this looks like in practice. Attackers manipulated the ANTHROPIC_BASE_URL environment variable to redirect Claude Code traffic to a server they controlled, quietly exfiltrating API keys in the process. That vulnerability existed because credentials persisted across sessions instead of being scoped tightly to the execution context that actually needed them.

The fix is architectural, not procedural. Agent config files should reference credential identities or scopes, never credential values. The YAML names which scope an agent needs, read access to a specific repository, write access to a specific S3 bucket, and the execution platform resolves that scope to an actual, freshly minted credential only at session start, revoking it when the session ends. Nothing sitting in the committed file can be replayed by someone who reads the repository, because there's nothing there worth replaying.

CVE-2025-59536 reinforces the same lesson from a different angle: remote code execution through a malicious project configuration file, triggered before any trust dialog even appeared. Config files are themselves an attack surface, which is exactly why they shouldn't also be where secrets live.

The memory field deserves the same scrutiny. Agent memory paths, something like .claude/memory/agent-name/, should stay scoped per agent rather than shared across them. Cross-agent memory access is an information boundary violation, and it's one that schema design can rule out structurally instead of relying on every agent's prompt to respect a boundary it was never actually denied access to.

Trigger conditions as the schema layer that defines when an agent is allowed to act

Hooks are event-driven automation defined right in the YAML frontmatter. They fire in response to specific events, a pull request opening, a test failing, a scheduled cron job, a manual command, rather than running continuously in the background waiting for something to do.

Trigger conditions are governance, full stop, not just a scheduling convenience. An agent without defined triggers can be invoked by anyone, at any time, against any context it happens to have access to. Defined triggers encode actual organizational policy into the file: this agent runs when a PR opens, not on every push to main; this agent runs inside CI, never inside a developer's local session where oversight is thinner.

Different trigger types carry different governance weight. Event triggers, a GitHub webhook, a CI signal, mean the agent only acts when a real engineering event has actually occurred, which keeps scope bounded and gives every action a traceable cause. Scheduled triggers run at fixed times, which makes them easy to rate-limit, monitor, or disable outright without touching the agent's underlying logic. Manual triggers require an actual human to initiate the run, and the schema can mandate this specifically for high-risk operations, building a human checkpoint into the system rather than hoping someone remembers to check in.

The OWASP Agentic Top 10 for 2026 names over-reliance on autonomous decisions as one of its top risks, and mandatory manual triggers for high-stakes actions are the schema-level way of implementing human oversight rather than just describing it in a policy document nobody reads. The trigger field is where a team decides, explicitly, which human decisions an agent is authorized to replace and which it isn't. Leave it implicit, and that decision hasn't been made. It's only been postponed.

What audit logging requires from the schema, and what the schema cannot provide alone

Several schema fields feed audit quality directly. The name slug means every log entry attaches to a named, identifiable agent instead of an anonymous process nobody can trace back. The tool allowlist means the full set of possible tool calls is known in advance, so anomaly detection can flag anything outside that declared set as worth a second look. Trigger conditions mean every session has a documented initiating event, which is the difference between being able to answer "why did this run?" and not. The version field in the Managed Agents platform, using optimistic concurrency through a version number, makes every update traceable; a 409 mismatch surfaces a conflicting write before it silently overwrites a governance decision someone else just made.

But schema design has a ceiling. It cannot provide runtime observability of every tool call, every diff, every thinking token generated mid-session; that requires the execution platform itself to log at the session level, not just at the config level. A perfectly designed YAML file describes what an agent is allowed to do. It says nothing about what actually happened during a given run unless something downstream is watching and recording it.

That gap is wider than it should be. Per the EY/AIUC-1 Consortium survey from March 2026, only 38% of organizations monitor AI traffic end-to-end across prompts, tool calls, and outputs, and only 17% continuously monitor agent-to-agent interactions. A well-designed schema is necessary for auditability. It is not sufficient. An agent with a flawless YAML definition, running on infrastructure nobody's actually watching, still produces a log gap exactly where an investigator would need one filled.

Schema fields should be designed with whoever reads the resulting log in mind, which means field values need to be human-readable and consistent, not generated slugs or opaque IDs that require a lookup table to decode during an incident. The same EY/AIUC-1 study found that 80% of surveyed organizations had documented risky agent behaviors, including unauthorized system access, the exact category of failure that a schema-level tool restriction would have prevented structurally, before it ever became a behavior to document.

Versioning and conflict resolution as governance mechanisms for teams managing multiple agents

A team running one agent can get away with treating its config file casually. A team running a dozen agents across several repositories cannot, because multiple engineers editing the same agent definition at the same time is not a hypothetical; it's a Tuesday.

This is where the version field in the Managed Agents platform schema earns its place. Optimistic concurrency control means each update to an agent's configuration carries a version number, and a write that doesn't match the current version gets rejected with a 409 conflict rather than silently overwriting whatever someone else just changed. That matters enormously for governance specifically, because the thing being overwritten in a silent conflict is rarely a cosmetic setting. It's a tool restriction, a budget cap, a trigger condition, exactly the fields this piece has spent the most time on.

Without that mechanism, two engineers can both believe they've locked down an agent's permissions, and only one edit survives. The other one, and whatever safeguard it encoded, disappears without anyone noticing until an incident traces back to a permission that should have been revoked weeks earlier. Versioning turns that failure mode from a silent one into a loud one: someone gets a conflict error, has to look at what changed, and has to reconcile it before the write goes through.

That reconciliation step is the governance mechanism, not the version number itself. Forcing a human to look at a conflicting change before it lands is a small piece of friction with an outsized payoff, because it's precisely the moment where a governance decision made by one engineer and a governance decision made by another engineer would otherwise collide invisibly. At the scale of a handful of agents, that friction feels like overhead. At the scale of dozens of agents maintained by a team that turns over, changes ownership, and forgets why a given restriction exists, it's the only thing standing between a config file and a config file nobody can trust.

Sources

  1. platform.claude.com
  2. claude-world.com
  3. genai.owasp.org

More in Agent-as-Code Patterns