AI Guardrails: Making Boundaries the Machine Cannot Cross In short: An agent cannot see your architecture. It sees files. Give it a map in the rules file, a way to check its work, and a linter that stops it — in that order, because only the last one actually stops it. Chapter 1 set your assistant up to write Angular 22 instead of Angular 15. That fixed syntax . This lesson fixes structure . The two failure modes are different. Stale syntax is obvious once you look — is right there in the decorator. A boundary violation is invisible. The code compiles, the tests pass, the feature works, and you have quietly coupled two domains together in a way that will cost a rewrite in eighteen months. Why agents cross boundaries by default Give an agent this task: > "Add the milk grade to the tanker status card in the shared UI library." It will do exactly what you asked. It finds in , imports it into , adds an input, updates the template. Clean code. Good naming. Tests pass. And it has just put a dairy business concept into your framework-agnostic library, which every other consumer app now inherits. The agent did nothing wrong by its own lights. It optimised for the task in front of it. It had no way to know that is a published product with a rule about business terms, because that rule is not in the code . It lives in your head, in this course, and in a wiki page nobody fed it. This is the general shape of the problem: models optimise locally, and architecture is a global constraint. Layer 1: Put the map in the rules file Extend the you started in Chapter 1 with the structure, not just the syntax. Describe boundaries as directions, and be specific about paths: That last block is doing the heavy lifting. It does not just state a rule — it tells the agent what to do instead when the rule blocks it. Without that, a model faced with a constraint tends to either ignore it or stop entirely. With it, you get a proposal. Layer 2: Give it a way to check A rules file describes the workspace as it was when you wrote the file. Repositories move. The Angular MCP server's tool reports what actually exists right now — applications and libraries in the workspace — so an agent can orient itself instead of guessing at paths. Pair it with so it can run the lint itself: Steps 4 and 5 are the ones that matter. An agent that runs the linter finds its own boundary violation and fixes it before you ever see the diff. An agent that does not runs out of feedback the moment it stops typing. Layer 3: The linter that actually stops it Layers 1 and 2 improve the odds. This one sets the floor. The config from the previous lesson is already the guardrail — you do not need a separate AI-specific rule. That is the point worth internalising: a good architectural guardrail does not care who wrote the code. The same rule that catches a rushed human catches a confident agent. Two additions are worth making specifically because agents are fast and prolific. Ban business vocabulary in the library by lint , so the failure names the actual problem: The field is not decoration. When an agent runs the lint and reads the output, that sentence is the correction it acts on. A message that says "use a presentation model instead" produces a fix; a message that says "restricted import" produces a retry of the same mistake. Protect the public API with a code owner , so nothing new gets exported without a human: An agent can write library internals all day. It cannot widen the public surface without someone signing off, because that is the one change that is genuinely permanent. Layer 4: The CI check Same job as Chapter 1's pipeline, one addition — the architecture graph: catches the failure mode that boundary tags miss: two files inside the same tag importing each other. Agents produce these more often than people do, because they add an import to solve an immediate problem without holding the whole graph in mind. "But I thought giving the AI more context would solve this" This is the natural conclusion after Chapter 1, and it is half right — context genuinely does fix the syntax problem. If the model can search the docs, it stops writing . Why would architecture be different? Because syntax and architecture fail differently. Syntax errors are local and self-evident. is wrong on its own, in isolation, no matter what file it is in. Any amount of correct context fixes it, and the compiler or linter catches what slips through. Boundary violations are correct locally and wrong globally. is a perfectly good line of TypeScript. It resolves. It type-checks. It is only wrong because of a decision made elsewhere about what this library is for . No amount of reading the file tells you that. And there is a scaling problem underneath. Your rules file is a few hundred lines. Your workspace is tens of thousands. As a session runs long, the constraint from turn one competes with forty turns of code, and specific concrete code beats an abstract rule every time. The failure is not tha…