The week after a security fix ships is when a coding agent can undo it: asked to make a failing test pass, an agent may remove the check that failed. Guardrails for AI coding agents are 3 layers that stop this: a rules file the agent reads, hooks and permissions in Cursor or Claude Code, and a CI check that fails the build whatever the agent was told.

Guardrails for AI coding agents: three layers, and which one actually binds

Guardrails for AI coding agents are the controls that keep an agent’s changes inside limits the team set. They come in 3 layers: a rules file the agent reads, which is advice; hooks, permissions and sandboxing in the tool, which bind that tool only; and pre-commit and CI checks with branch protection, which bind every change.

The meaning of guardrails in software comes from the road: a control that lets work move fast inside a boundary and stops it at the edge. A gate is the other kind of control, and it stops everything until someone approves it. Repository guardrails are one of the controls in the engineering standards for AI-assisted teams, the one aimed at the agent that edits the code.

The table is my reading of who each layer binds and how an agent gets past it. The instruction layer is weak by design: Claude Code’s documentation on CLAUDE.md files says Claude treats them as “context, not enforced configuration”, and that “there’s no guarantee of strict compliance, especially for vague or conflicting instructions”.

LayerExamplesWho it bindsHow an agent gets past it
InstructionCLAUDE.md, AGENTS.md, Cursor rules, Copilot’s instructions fileNobody. The agent reads it as contextIt weighs the rule against the task, and the task wins
ToolHooks, permission and deny rules, sandboxing, organization policyThat tool, on that machine or accountThe next change comes from another tool, or from a machine without the settings
RepositoryPre-commit checks, required CI checks, branch protection, code owners on protected pathsEvery change, whoever or whatever made itOnly through someone the branch rule exempts, such as an admin

The repository layer comes with a plan condition. GitHub’s docs say protected branches are available in public repositories with GitHub Free, and in public and private repositories with GitHub Pro, GitHub Team, GitHub Enterprise Cloud and GitHub Enterprise Server; rulesets and code owners follow the same public and private split. The same docs say that by default the restrictions of a branch protection rule don’t apply to people with admin permissions, unless you choose to apply them to administrators too. My reading of those two lines: on a private repository on GitHub Free, CI can report a failure but cannot block the merge, and an agent working under an admin’s account walks past a rule that exempts admins.

The reason to bother is plain: future AI-assisted changes can undo hardening unless the workflow checks them.

The AI coding assistant guardrails checklist I work from has nine lines, and the wording is my working rule rather than a standard.

  • A rules file exists and names the protected patterns.
  • The file is short enough that someone actually reads it.
  • Each protected pattern has a test or check behind it.
  • Destructive commands and secrets files are denied in the tool.
  • The agent has no production credentials.
  • Pre-commit checks run on every commit.
  • The same checks run again as required checks in CI.
  • Main is protected, and agents work through pull requests.
  • A person reviews anything that touches a protected path.

What agentic coding is, and what changes when an agent edits the repo

Agentic coding is software development in which an AI agent plans a task, edits files, runs commands and tests, reads the results and repeats until it judges the task done. The difference from autocomplete or chat is authority: the agent acts on the repository and the shell itself, so its mistakes arrive as commits.

The older kind of AI coding helper is autocomplete or chat: the tool suggests code and a person applies it. An agentic AI coding assistant, sometimes called an agentic coder, plans the work, edits many files, runs the shell and the tests, and goes round again until it decides it is done. An agentic IDE is an editor built around that loop: Cursor’s documentation describes its Agent as the assistant that “can complete complex coding tasks independently, run terminal commands, and edit code”. Terminal agents are the same idea without the editor. Claude Code’s overview calls it “an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools”, and Gemini CLI describes itself as “an open-source AI agent that brings the power of Gemini directly into your terminal”. Agentic programming and agentic coding are used for the same thing.

ModeWho runs commandsWho decides it is doneWhere mistakes land
AutocompleteNobody. It only suggestsThe person accepting the suggestionIn the line the person accepted
ChatThe person, if they copy the commandThe personIn whatever the person pasted
Agent (Cursor’s Agent, Claude Code, Gemini CLI)The agent, in your shellThe agent, when its own checks passIn commits across many files

If all you want is coding help from an AI on one function, the first two modes are enough, and the risk stays with the person who applies the code.

The meaning of agentic coding for a repository, in my reading, comes down to four changes. The agent has the developer’s file and shell access. It works faster than anyone reads the diff. It optimizes for the goal it was given, a green test, over the one nobody stated, the check that test was guarding. And it carries no memory of why last month’s fix exists unless the repository tells it. Agentic coding and vibe coding describe different things: the first is how the tool works, the second is how much of the output the person reads.

The other meaning: guardrails for the AI agents inside your product

A search for this phrase also returns runtime guardrails: filters on what goes into and comes out of a model your app calls, and limits on the tools an agent inside your product may use. That is a different control on a different system, and it sits with prompt injection in what prompt injection is. Everything below is about the agents that write your code.

What goes wrong without it

If your AI coding assistant undid a security fix, start with row one of the table. The other four rows are changes that arrive the same way: the agent was asked for one thing, did it, and changed something else on the way.

What you asked forWhat the agent changed as wellWhy nothing caught it
Fix a failing file uploadWidened a storage policy, or moved a server-side check back into the clientThe fix had no test, so nothing went red
Make the test suite passDeleted the failing test, or marked it to skipNo check notices a test that disappears
Add a featureA second data-fetching pattern, a third date library, a new folder layoutNothing told it the conventions, and nothing fails when they are broken
Get a deploy throughRemoved a rate limit, or turned type checking or lint off at build timeThe build config is not a protected path
Reset some test dataRan a migration reset or a force push against a real environmentThe agent held credentials for that environment

AI-generated code keeps breaking conventions for the reason in the third row: the conventions live in someone’s head, and no check fails when new code ignores them. None of the five rows needs bad faith from the agent or carelessness from the person who prompted it: in each one, the repository allowed the change.

Row one, told as a case. A team’s CLAUDE.md says the plan-limit check must stay on the server, and in a new session a coding agent is asked to make a failing test on the upgrade screen pass. The file is context, not an enforced rule, so the agent moves the check into the page to get the test green; no CI check guards the server-side check, so the pull request passes and merges. The lesson I take from it: a rules file can be ignored, and only a required check refuses the change.

The gate that would catch these rows was missing in most of the apps I audited. At least 17 of the 21 third-party apps had no deploy gate, and so did all 5 founder apps: every push ships straight to production with nothing checking it first. Several of the audited apps disable their own type and lint checks at build time. The 21 are the 11 public and 10 held-out third-party apps I audited in June and July 2026, a selected set rather than a random sample, and not a rate for AI-built apps in general.

The agentic coding workflow: plan, patch, check, and the loop that needs a gate

An agentic coding workflow is a loop of 4 steps: plan the change, patch the files, check by running tests and linters, and repeat or hand over. The agent grades its own work inside that loop, so the loop needs a gate outside it: a required CI run and a human approval on the pull request.

The table is my reading of where each guardrail sits in the loop.

Step in the loopWhat the agent doesThe guardrail at that step
PlanReads the rules file and the task brief, and proposes the changesProtected patterns named in the rules file; a plan reviewed before any edit on risky work
PatchEdits files and runs shell commandsTool permissions, denied paths, no production credentials within reach
CheckRuns the tests and lintersChecks that exist, and that cannot be edited in the same change without a flag
Hand overOpens a pull request, or pushesA pull request, required CI, and a person on protected paths

Agentic development and an agentic SDLC are the same loop seen at team scale: the software development lifecycle with AI agents doing the build and test steps. That moves the human work to the two ends of the loop, the brief going in and the review coming out.

Two vendors publish guidance worth reading before you write your own agentic coding best practices. Anthropic’s Claude Code best practices for agentic coding now live in the Claude Code docs, and three of its headings carry the point: “Give Claude a way to verify its work”, “Explore first, then plan, then code” (its four phases are Explore, Plan, Implement and Commit), and “Add an adversarial review step”, which has a subagent review the diff in a fresh context. The same page puts the layer argument in one line: “Unlike CLAUDE.md instructions which are advisory, hooks are deterministic and guarantee the action happens.” GitHub’s docs on Copilot’s agent say Copilot cloud agent can “research a repository, create an implementation plan, and make code changes on a branch”, and that “You can review the diff, iterate, and create a pull request when you’re ready.”

Agentic engineering patterns worth keeping, one line each, as my working rules:

  • Small tasks, each with a stated done condition.
  • Tests first, so the agent has a check to run.
  • A fresh context for each task.
  • A second agent or a person as the reviewer, never the author.

How to do it: the rules file, the tool, the commit, and the build

The four sections below go in order of increasing force. Before any terminal work, open the repository on GitHub and look for three things: a rules file at the root, a checks section on the latest pull request, and a rule protecting the main branch. GitHub’s docs put branch rules under Settings, then Branches in the “Code, planning, and automation” section of the sidebar. Each one missing is a layer missing, and the plan condition from the first section decides whether the last one can exist on your repository.

How to set coding rules for AI assistants: the rules file and its protected patterns

Coding rules for AI assistants live in a file at the repository root that each tool reads: CLAUDE.md, AGENTS.md, Cursor rules or Copilot’s instructions file. The useful part is not style. It is the protected patterns: the files, checks and settings the agent must never change without asking, each with the reason and the test that guards it.

Which file each tool reads, and what happens when two coexist, is covered in which file each coding tool reads. The sections of a CLAUDE.md, including the guardrail sections most files leave out, are in CLAUDE.md best practices. Cursor’s rule types and format are in Cursor rules best practices, and the cross-tool file is covered in what AGENTS.md is and whether it helps. What this page adds is the list of what the file must protect, with the check behind each line.

The rows below are the protected patterns a hardened AI-built app usually needs, as my working rule. The third column is the check from the CI section further down.

Protected patternWhy it existsThe check that guards it
Row-level security policies and storage policiesThey decide who can read and write each row and fileA test that signs in as a second user and tries to read the first user’s data, and gets nothing back
Server-side authorization checksAnyone can edit what runs in the browserA test that calls the endpoint as the wrong user and expects a refusal
The webhook signature checkIt stops forged payment eventsA test that posts an unsigned event and expects a refusal
Rate limits and usage capsThey cap abuse and the billA config assertion that each limit is still present
Environment and secrets handlingServer-only keys must never reach the browserThe secret-in-client-code check
The migrations folderMigrations change live dataCode owner review on the folder
CI workflow files and the rules file itselfAn agent can weaken its own checksCode owner review on both
TestsThey are the evidenceTests may be added; a deleted or skipped test fails the build

Each row carries its reason and its check because a rule with a reason is easier to follow than a bare prohibition. Claude Code’s docs say it in their own words: “The more specific and concise your instructions, the more consistently Claude follows them.” Here is a constructed excerpt, with made-up paths, of how the section reads in a rules file:

## Protected patterns (ask before changing any of these)
- supabase/migrations/**: never edit an applied migration.
  Check: code owner review.
- The plan-limit check in src/server/billing.ts stays on the server.
  Check: tests/billing-limits.test.ts calls the API as a free user.
- verify_jwt stays on for every Edge Function except stripe-webhook.
  Check: the config assertion in CI.
- .github/workflows/**, CLAUDE.md, AGENTS.md: no edits without asking.
  Check: CODEOWNERS.
- Tests: add freely. Never delete, skip or focus one. Check: CI.
- Never read .env files. Server keys never appear in src/components.
  Check: scripts/check-forbidden.mjs and the tool's deny rules.

For a Claude Code session, my checklist is four lines: plan first, keep the task small, run the checks, and read the diff before it becomes a commit.

Shared rule collections are a source of ideas and a risk at the same time. A community collection of Cursor rules, the awesome-cursorrules repository, is published under CC0-1.0. Read every line before you paste any of it: a rules file is instructions the agent will follow, so a hostile line in a shared file is a prompt injection route with no code in it, and the control is a person reading it plus code owner review on the rules file. The rules file should point at a README and a map of the repository rather than repeat them, and what belongs in a README is a separate question.

Per-tool enforcement: Cursor hooks, Copilot settings and Claude Code permissions

Per-tool enforcement is what the tool itself refuses or intercepts, whatever the rules file says. Cursor hooks can block an agent action, Claude Code has permission rules, hooks and a sandbox, and Copilot Business and Enterprise add content exclusion and organization policies. Each binds only that tool: a commit made with another tool is not covered.

ToolRules file it readsHooksPermission or deny rulesSandboxOrganization policy
CursorProject Rules in .cursor/rules; AGENTS.md.cursor/hooks.json; events such as preToolUse, beforeShellExecution and beforeReadFile; exit code 2 blocks the actionRun modes Auto-review, Allowlist and Run Everything; File-Deletion Protection and External-File Protection; permissions.json with plain-English block_instructionsmacOS (Seatbelt) and Linux (Landlock and seccomp)An enterprise hooks.json at a system path; Team Rules on Team and Enterprise plans
Claude CodeCLAUDE.md; it can read AGENTS.md on its own or alongsidePreToolUse, PostToolUse and others in .claude/settings.json; exit 2 on PreToolUse blocks the tool callAllow, ask and deny rules, checked deny first, such as Read(./.env)Bash commands and their child processes, on macOS, Linux and WSL2; not native WindowsManaged settings that user and project settings can’t override, apart from a few security-sensitive keys
GitHub Copilot.github/copilot-instructions.md, .github/instructions/*.instructions.md, AGENTS.md.github/hooks/*.json for the cloud agent and Copilot CLI; preToolUse can approve or denyThe cloud agent pushes only to a single branch, a new copilot/ branch unless it was called on an existing pull request, and cannot approve or merge its pull request; content exclusion (Business and Enterprise), not supported in Edit and Agent modes of Copilot Chat in editorsGitHub restricts the cloud agent’s access to the internetOrganization policies for features and models (Business and Enterprise)

Docs checked on 27 September 2026: Cursor’s hooks, run-mode and rules pages; Claude Code’s permissions, hooks and sandboxing pages; and GitHub’s pages on Copilot custom instructions, hooks, the cloud agent, its risks and mitigations, content exclusion and organization policies.

Cursor’s hooks documentation says hooks run before or after stages of the agent loop and “can observe, block, or modify behavior”, and a hook script that exits with code 2 blocks the action, the same as returning a deny permission. Cursor agent hooks live in .cursor/hooks.json in the project, in your home folder, or at an enterprise path on the machine. The run mode, Allowlist included, is picked under Settings, Agents, Approvals & Execution, and the same docs page lists File-Deletion Protection and External-File Protection. Whether Cursor as a whole is safe for your code is a separate question, answered in is Cursor safe: the settings that decide the agent’s authority.

In Claude Code a deny rule wins over any allow rule, because rules are evaluated deny, then ask, then allow; Claude Code’s permission settings show where each rule comes from. The memory page names the hook to use in one line: “To block an action regardless of what Claude decides, use a PreToolUse hook instead.” File deny rules cover Claude’s own file tools and the shell commands it recognizes, not a script that opens files by itself, and the sandbox is the operating-system backstop for that case. Whether those rules count as real enforcement is argued in full in is Claude Code safe.

For GitHub Copilot, the repository instructions live where GitHub’s guide to Copilot custom instructions puts them. The cloud agent is available for all paid Copilot plans, and the enforcement sits around it: it can only push to a single branch, it “cannot approve or merge a pull request”, and by default its workflows do not run until someone with write access approves them. Content exclusion is for organizations on Copilot Business or Copilot Enterprise, and GitHub’s docs say it “is currently not supported in Edit and Agent modes of Copilot Chat in Visual Studio Code and other editors”. The GitHub Copilot best practices page includes “Use automated tests and tooling to check Copilot’s work.” What each plan covers is set out in GitHub Copilot security risks by plan.

The rule I apply in all three tools: deny reads of secrets files, deny destructive shell commands, and keep production credentials out of the agent’s environment altogether. The limit of this layer fits in one sentence: it binds one tool, and the next contributor may use another.

Husky and lint-staged: the pre-commit layer, and why CI has to repeat it

Husky with lint-staged runs checks on staged files at commit time, so a forbidden pattern is caught before it leaves the laptop. It is a convenience layer, not a control: a commit can skip the hooks with a flag, and a commit made where the hooks were never installed never runs them. The same checks must run again in CI.

Husky installs Git hooks from files kept in the repository. Husky’s documentation says npx husky init “creates a pre-commit script in .husky/ and updates the prepare script in package.json”. lint-staged then runs your commands against the staged files only, and passes their names as arguments. The pre-commit set I’d use on an AI-built app, as my working rule: format and lint the staged files, run the project’s own type check, run a secret scan, and run the forbidden-pattern script from the next section. An agent that commits from the terminal triggers the hooks like anyone else.

A constructed config, following the two projects’ docs:

// lint-staged.config.js (ESM; without "type": "module" in package.json, use module.exports)
export default {
  '*.{js,jsx,ts,tsx}': [
    'eslint',
    () => 'npm run typecheck', // a function, so no file list is appended and tsconfig applies
    () => 'node scripts/check-forbidden.mjs',
  ],
};
// .husky/pre-commit holds one line: npx lint-staged

The function form matters for the type check: the lint-staged README warns that “Certain input files can cause TypeScript to ignore tsconfig.json”, and a function stops the file list being appended. Why the layer is not a control: Husky’s how-to page says “Most Git commands include a -n/—no-verify option to skip hooks”, and HUSKY=0 disables them for a command. A fresh clone that never ran the install has no hooks at all. Hence my rule: pre-commit is for speed, CI is for truth. Which linter rules to switch on is a separate topic: linters for a codebase an AI wrote.

How would you harden AI changes before review: the CI check that fails the build

A CI guardrail is a required check that fails the build when a protected pattern is broken, no matter who or what made the change. Start with 5: secrets in client code, a disabled security setting, a deleted or skipped test, a change under a protected path without its owner’s review, and a new dependency with a known vulnerability.

Each check runs without anyone reading the diff line by line.

Forbidden regressionHow CI detects itWhat the failure says
A server-only secret or service key referenced in client codeAn ESLint no-restricted-imports or no-restricted-syntax rule, or a script that searches the client folders for the server-only variable names and the sb_secret_ prefixSecret in client code. See rules file: Secrets.
A security setting switched offA config assertion: the row-level security test still passes, no new verify_jwt = false outside the listed functions, no ignoreBuildErrors: true, no lint switched off at buildSecurity setting changed. See rules file: Protected patterns.
A test deleted or marked skipCompare the test files with the base branch, and search the tests for .skip and .onlyTest removed or skipped. See rules file: Tests.
An edit to a protected path that its code owner has not approvedCODEOWNERS plus a branch rule that requires code owner reviewGitHub holds the merge until the owner approves
A new dependency with a known high-severity vulnerabilitynpm audit --audit-level=high with the lockfile committed; pip-audit on PythonThe audit’s report and a non-zero exit code

The secret check needs the right strings. Supabase’s API keys page says the new secret keys start sb_secret_ and are “short strings, not JWTs”, while the legacy service_role key is a long-lived JWT, and a long key that “begins with eyJ” belongs to the legacy keys, the public anon key included. So in my reading neither the word service_role nor the eyJ prefix finds a leaked server key on its own; the script matches the server-only variable names your code uses and the sb_secret_ prefix. In a JavaScript codebase, ESLint’s no-restricted-imports rule can also ban a server-only module from client files, with a custom message per path.

The security-setting check reads config. Supabase Edge Functions require a valid JWT by default, and verify_jwt = false in supabase/config.toml turns that off, which Supabase’s docs show for a Stripe webhook; the assertion allows the functions you list and fails on any other. Next.js’s typescript.ignoreBuildErrors defaults to false, and when it is switched on the docs say it “bypasses the check entirely”.

For the dependency check, npm’s --audit-level sets “The minimum level of vulnerability for npm audit to exit with a non-zero exit code”, and “By default npm requires a package-lock or shrinkwrap in order to run the audit”, so the repository commits its lockfile. The pip-audit README documents no severity level, only exit code 1 when “One or more known vulnerabilities were found”, so on Python the check fails on any known vulnerability. The protected-path check is CODEOWNERS plus the branch rule; setting both up is covered in GitHub branch protection, and the plan condition from the first section applies to both.

Here is a constructed example of the forbidden-pattern script, not taken from a real repository; adapt the folders and names:

// scripts/check-forbidden.mjs (constructed example)
import { readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';

const rules = [
  ['src/components', /sb_secret_|SERVICE_ROLE_KEY/, 'Secret in client code. See rules file: Secrets.'],
  ['tests', /\.(skip|only)\(/, 'Test removed or skipped. See rules file: Tests.'],
];
const walk = (dir) => readdirSync(dir, { withFileTypes: true }).flatMap((entry) =>
  entry.isDirectory() ? walk(join(dir, entry.name)) : [join(dir, entry.name)]);

let failed = false;
for (const [dir, pattern, message] of rules) {
  for (const file of walk(dir)) {
    if (pattern.test(readFileSync(file, 'utf8'))) { console.error(`${file}: ${message}`); failed = true; }
  }
}
process.exit(failed ? 1 : 0);

The failure message names the rule and the rules-file section, so the agent’s next turn can read why it failed; that is my working rule for every check. The smoke tests that guard the behavior itself are, in my reading, the strongest guardrail of all, and they belong in the same pipeline: the full list is in code quality checks, and building the pipeline is covered in CI/CD best practices.

Then the human layer. What a reviewer looks for in an agent’s pull request is in code review best practices, and if nobody on the team can read the code, the route is how to verify AI-generated code before production.

How to verify it

AI repository guardrails are verified by demonstration: a pull request that reintroduces a forbidden pattern fails CI and cannot merge, a commit that skips the local hooks is still caught, and an agent’s edit to a protected file is blocked or held for approval by the tool. Keep the failed run’s URL as the evidence.

  1. 01 Read the rules file against the protected-patterns table: every row present, each with its reason and a named check, and the file short enough to read in about five minutes. Evidence: the commit that added the file.
  2. 02 On a test branch, reintroduce one forbidden pattern on purpose, such as a server key name in a client file or a skip on a guarded test, and commit as usual. Pass: the pre-commit hook refuses the commit and names the rule. Evidence: the terminal output.
  3. 03 Commit the same change with git commit --no-verify to skip the local hooks, push it and open a pull request. Pass: a required check fails and names the rule, and the merge stays blocked. Evidence: the failed run's URL and the state of the merge button.
  4. 04 In the agent tool, ask for an edit to a protected file and a read of the secrets file. Pass: an approval prompt or a blocked action that the tool's permission rule or hook produces, named in the transcript. The agent declining on its own is not a pass, because that is the rules file working, not the tool layer. Evidence: the transcript.
  5. 05 Have the agent open a pull request for a harmless change. Pass: it arrives as a pull request with checks, not as a push to main. Evidence: the pull request.

A blocked merge in step 3 needs the branch rule, so the plan condition from the first section applies, and if the agent works under an admin’s account the rule has to apply to administrators as well. With Copilot’s cloud agent, by default the checks in step 5 start only after someone with write access approves the workflow run. Close the test branches without merging them.

In the Production Hardening Sprint, deliverable 10.8 is verified this way: review the guidance and demonstrate CI catching a representative forbidden regression.

Where the sprint does this

In the sprint, deliverable 10.8 provides CLAUDE.md, AGENTS.md, Cursor rules, or equivalents describing conventions and protected patterns, and adds CI checks for enforceable rules, verified as described under How to verify it. Deliverable 7.3 runs linting, type checks, builds, and tests on every pull request, and deliverable 7.2 protects the main branch with required checks and controlled merge permissions. Each is recorded in the production readiness report, deliverable 13.1, which accounts for all 123 IDs, keeps failures visible until resolved and explains genuine non-applicable items. Building new product features or modules sits outside the sprint. The whole list is in the AI guardrails check in the published scope.

Common questions about agentic coding and its guardrails

Is ChatGPT an agentic AI?

Partly. In a plain chat, ChatGPT answers and a person applies the code; the agentic part is OpenAI’s coding product, Codex, and the overview of OpenAI’s Codex docs says “ChatGPT can gather context, take action, and produce something useful.” In my reading, the guardrails on this page apply to any of these tools once it can push to your repository.

How do I get started with agentic coding?

Put the guardrails in before the agent, in this order: tests on the critical paths, a rules file with protected patterns, branch protection with required CI, and only then small tasks through pull requests. That order is my working rule, because the first three give the agent something to fail against before it writes anything. Begin with one task that has a clear done condition, and read the whole diff.

Is agentic coding good?

In my reading, agentic coding is good at well-specified changes with tests to run against, and weak at knowing why an existing check exists. Whether it is good for your codebase depends less on the model than on the repository: with required checks and protected paths, an agent’s mistake fails a build, and without them it ships. No tool was tested or ranked for this page.

Can you give me an example of a guardrail in AI?

Two examples, one for each meaning of the word. For coding agents: a required CI check that fails the build when a server-only key name appears in client code. For AI agents inside a product: an output filter on what the model returns, or a permission that limits which tools the agent may call, which belongs with prompt injection defenses.