Two patterns appear at almost every decision point in an agentic system: routing (pattern 2 in the Gulli taxonomy) and guardrails (pattern 18). They are not the same thing, but they work together. Routing without guardrails produces efficient but ungoverned execution. Guardrails without routing produce governance that applies indiscriminately to everything, regardless of risk. The combination produces a system that routes work to the right place and checks it against the right rules before allowing it to proceed.
This article covers both patterns through the lens of how ticketyboo.dev implements them: the Gatekeep governance engine, the fixer-bot classifier, and the declarative rules model that has survived multiple infrastructure replacements unchanged.
Routing: classify before you execute
The routing pattern has a single core principle: classify the input before deciding what to do with it. An unclassified request is handled by whatever is available. A classified request is handled by what is appropriate. The difference in outcome is significant, and the cost of the classification step is almost always justified.
Gatekeep implements routing through three specialised personas, each responsible for a distinct domain of decision-making:
- Sentinel: Security concerns. Credential exposure, IAM policy violations, network exposure, dependency vulnerabilities. Sentinel has a short list of things it looks for and a clear set of responses (flag, block, escalate).
- Auditor: Cost and compliance. Free Tier breaches, tagging violations, policy drift, audit trail gaps. Auditor has context about the budget envelope and compliance requirements that Sentinel does not need.
- Architect: Design decisions. Service selection, dependency choices, architectural trade-offs. Architect has context about the platform's design principles and constraint set that neither Sentinel nor Auditor needs.
The classifier sits upstream of all three. It reads the incoming work item (a change, a deployment request, a code review, a cost alert) and determines which persona or personas should handle it. A change that introduces a new AWS service goes to both Auditor (cost check) and Architect (design review). A change that modifies an IAM policy goes to Sentinel. A change that refactors an internal module goes to Architect only.
The fixer-bot classifier: routing by capability requirement
The Gatekeep persona model routes by domain (what kind of concern is this?). The fixer-bot classifier routes by capability requirement (how much capability does this task actually need?). These are different questions and they produce different routing decisions.
The fixer-bot takes GitHub issues and generates implementation plans. Not all issues are equally complex. A typo fix in a configuration file is simple: one file, one change, deterministic output. Refactoring a Lambda handler to add a new endpoint is medium: multiple files, some judgment required, tests to update. Designing a new auth flow is complex: multiple components, architectural decisions, potential security implications.
Running every issue through the heaviest available model is wasteful. Running every issue through the lightest model produces poor results on complex tasks. The classifier reads the issue and assigns a tier based on a rubric: number of files likely affected, presence of architectural keywords, existence of external dependencies, and whether the issue references security or cost concerns.
# fixer-bot tier classification (simplified)
# Simple: 1 file, no external deps, no architectural keywords
# Medium: 2-5 files, some judgment required, tests affected
# Complex: 5+ files, architectural keywords, security/cost flags
def classify_issue(issue: dict) -> str:
"""Route to execution tier based on capability requirement."""
title = issue.get("title", "").lower()
body = issue.get("body", "").lower()
text = title + " " + body
security_flags = any(k in text for k in (
"auth", "iam", "credential", "secret", "permission", "security"
))
arch_flags = any(k in text for k in (
"refactor", "migrate", "redesign", "architecture", "pattern", "introduce"
))
file_count_hint = text.count(".py") + text.count(".tf") + text.count(".html")
if security_flags or arch_flags or file_count_hint >= 5:
return "complex"
elif file_count_hint >= 2 or any(k in text for k in ("test", "update", "extend")):
return "medium"
else:
return "simple"
Tier assignment drives model selection, context budget, and review requirements. Simple tasks get a fast, cheap execution path with no human review step. Medium tasks get a mid-tier model with automated test validation. Complex tasks get the heaviest model, a planning phase before implementation, and a mandatory human review gate before merge.
The important design choice: tier is a proxy for capability requirement, not for importance. A simple bug fix on a critical path is still Simple tier. It gets a fast, cheap execution path because that is all it needs. The review gate is not determined by how critical the code is. It is determined by how much autonomous judgment the task requires.
Guardrails: what the rules look like in practice
The guardrails pattern covers mechanisms that constrain agent behaviour within defined boundaries. The word "guardrails" suggests passive barriers. In practice, the most effective governance systems are active: they check, they classify, they route, and they either approve automatically or escalate for human review.
Gatekeep's rules model is declarative JSON. Each rule has a condition, a domain (which persona owns it), a severity, and an action (allow, block, escalate, flag). A simplified example from the cost rules:
# gatekeep cost rules (simplified)
{
"rules": [
{
"id": "cost-001",
"name": "New AWS service requires cost assessment",
"domain": "auditor",
"condition": "change introduces AWS service not in approved_services list",
"severity": "high",
"action": "escalate",
"message": "New service {service} not in approved list. Auditor review required."
},
{
"id": "cost-002",
"name": "Lambda memory above threshold",
"domain": "auditor",
"condition": "lambda.memory_size > 256",
"severity": "medium",
"action": "flag",
"message": "Lambda {name} configured at {memory}MB. Free Tier optimum is 128MB."
},
{
"id": "sec-003",
"name": "Secrets Manager blocked",
"domain": "sentinel",
"condition": "resource.type == 'aws_secretsmanager_secret'",
"severity": "high",
"action": "block",
"message": "Secrets Manager ($0.40/secret/month) is blocked. Use SSM Parameter Store SecureString."
}
]
}
The rules describe intent, not implementation. The enforcement engine reads the rules and evaluates them against the incoming change. The same rule file has been active through multiple changes to the underlying infrastructure: from Paperclip on Lightsail to Lambda functions, from manual deployment to Terraform-managed resources. The rules did not change when the execution engine changed.
Guardrails layer: where checks sit in the pipeline
Gatekeeper, not gate-blocker
The most common failure mode in governance systems is excessive friction. A gate-blocker stops every change until a human approves it. This produces compliance theatre: approvers approve without reading because the volume is too high for careful review. Approval becomes a rubber stamp, and the governance is illusory.
A gatekeeper is different. It routes work through the appropriate checks, approves automatically when rules are satisfied, and escalates to humans only when the rules are not satisfied or when the risk level warrants it. The human review queue contains real decisions, not routine approvals. Because the queue is short, the reviews are meaningful.
In practice, this means most changes in a well-governed system proceed without human intervention. A Lambda function that stays within the existing memory tier, uses SSM Parameter Store for secrets, includes required tags, and modifies files within an existing service boundary clears all automated checks and deploys without waiting for a review. The developer gets fast feedback that their change is compliant. Governance does not slow them down.
A change that introduces a new AWS service fails the cost-001 rule (new service not in approved list) and routes to the Auditor for a cost assessment. A change that adds an IAM policy fails the security scan and routes to the Sentinel. These changes represent genuine decisions that benefit from review. The rule system filters for them.
Declarative rules survive infrastructure changes
The ticketyboo.dev governance rules were defined while Paperclip ran on a Lightsail instance. They ran through the migration to Lambda functions. They will run through whatever comes next. This is not accidental. It is a consequence of keeping rules declarative and keeping the enforcement engine separate from the rule definition.
Declarative rules describe what is allowed. The enforcement engine reads the rules and evaluates them against the incoming change. Changing the enforcement engine does not require changing the rules. Adding a new rule does not require changing the enforcement engine.
This is the architectural principle behind guardrails: the rules are the valuable part. They encode accumulated knowledge about what has gone wrong, what costs money unexpectedly, what creates security exposure. The enforcement mechanism is infrastructure. Infrastructure gets replaced. Rules accumulate.
Where routing and guardrails intersect
Routing and guardrails are often treated as separate concerns. In practice, the routing decision is itself a form of guardrails enforcement. When the classifier determines that a change touches IAM policies and routes it to Sentinel, it is also applying a rule: "IAM changes require security review." The route is the rule.
This means the routing logic should be considered part of the governance architecture, not separate from it. A routing decision that bypasses a persona that should be consulted is a governance failure. A routing decision that routes work to a persona with the wrong context is an efficiency failure that may also be a governance failure.
The combination that works: a classifier that routes by domain, a persona model where each domain has clear ownership and a defined rule set, and a rule model that is declarative and independent of the execution engine. Build those three components and the routing-and-guardrails pair becomes a durable foundation for governing agentic workflows at development speed.
If the articles or tools have been useful, a coffee helps keep things running.
☕ buy me a coffeeticketyboo brings governed AI development to your pull request workflow. 5 governance runs free, one-time welcome grant. No card required.
View pricing Start free →