How Ponytail Generates and Filters Rulesets for Different Agent Modes
Ponytail constructs agent rulesets by loading a base capability catalog from ponytail/ruleset.py, then applies mode-specific transformations defined in ponytail/modes.py to enable, disable, or filter rules based on the selected operational mode.
Ponytail is an open-source agent framework that governs AI behavior through configurable safety rules. The Ponytail ruleset generation and filtering system allows developers to define operational modes—such as lazy-senior-dev, debug, or test—that dynamically restrict or expand agent capabilities at runtime according to the DietrichGebert/ponytail source code.
Base Rule Collection
All primitive capabilities in Ponytail originate as rule dictionaries within the repository’s rules module. According to ponytail/ruleset.py, each rule defines a specific agent capability, its default safety classification, and descriptive metadata. These rules form the universal foundation upon which all mode-specific configurations are built.
Mode-Specific Augmentation
Ponytail ships with predefined operational modes stored in ponytail/modes.py. When the runtime initializes, it reads the PONYTAIL_MODE environment variable or the --mode CLI flag to determine which mode configuration to apply.
Rule Inclusions and Exclusions
Every mode object exposes two critical lists: add_rules and remove_rules. The add_rules list injects additional capabilities into the base ruleset, while remove_rules explicitly strips out dangerous or unnecessary permissions. For example, a test mode might remove filesystem_write while adding mock_operations.
Dynamic Rule Filtering
Beyond simple inclusion lists, each mode implements a filter_rule(name, rule) function that inspects rule metadata—including severity levels and tags—to determine whether a capability should remain active. This function receives the rule name and dictionary, returning a boolean indicating retention.
Final Ruleset Construction
The core build_ruleset(mode: Mode) -> Ruleset function in ponytail/ruleset.py orchestrates the assembly process. This function first loads the complete base catalog, then sequentially applies the mode's removal list, addition list, and filter function to produce the final restricted ruleset.
# Logic from ponytail/ruleset.py
base = load_all_rules() # Load universal capabilities
for r in mode.remove_rules: # Apply mode exclusions
base.pop(r, None)
for r in mode.add_rules: # Apply mode inclusions
base[r] = get_rule_definition(r)
# Apply mode-specific predicate filter
final = {name: rule for name, rule in base.items()
if mode.filter_rule(name, rule)}
return Ruleset(final)
The resulting Ruleset object attaches to the Agent instance (defined in ponytail/agents.py) and validates every operation attempt.
Practical Implementation Examples
Selecting Modes via CLI
Developers specify modes via command-line arguments or environment variables. The CLI parser in ponytail/cli.py resolves the mode name through the MODE_REGISTRY before constructing the agent.
# Run with the debug mode (allows broader system access)
ponytail run --mode debug
# From ponytail/cli.py
mode_name = args.mode or os.getenv("PONYTAIL_MODE", "lazy-senior-dev")
mode = MODE_REGISTRY[mode_name] # Defined in ponytail/modes.py
agent = Agent(ruleset=build_ruleset(mode))
Creating Custom Modes
Custom modes inherit from the base Mode class in ponytail/modes.py. Implementers override the three filtering mechanisms to create specialized safety profiles.
# custom_mode.py
from ponytail.modes import Mode
class StrictMode(Mode):
name = "strict"
add_rules = ["audit_logging"]
remove_rules = ["network_access", "filesystem_write"]
@staticmethod
def filter_rule(name, rule):
# Only retain rules tagged as "safe"
return "safe" in rule.get("tags", [])
Inspecting the Active Ruleset
Developers can examine the assembled ruleset to verify which capabilities are active for a given mode.
from ponytail.ruleset import build_ruleset, MODE_REGISTRY
mode = MODE_REGISTRY["debug"]
ruleset = build_ruleset(mode)
print(ruleset.enabled_rules())
# Output: ['shell_execute', 'http_get', 'file_read', ...]
Summary
- Ponytail maintains a base rule catalog in
ponytail/ruleset.pycontaining all possible agent capabilities. - Mode configurations in
ponytail/modes.pyspecify additions, removals, and filter predicates. - The
build_ruleset(mode)function composes the final ruleset by applying mode transformations sequentially. - Runtime selection occurs via the
--modeCLI flag orPONYTAIL_MODEenvironment variable. - Custom modes inherit from the
Modebase class and implementadd_rules,remove_rules, andfilter_rule.
Frequently Asked Questions
What is the default mode when running Ponytail?
When no mode is specified via the --mode flag or PONYTAIL_MODE environment variable, Ponytail defaults to lazy-senior-dev mode. This configuration balances productivity with safety by enabling common development tools while restricting dangerous system modifications.
Can I filter rules based on metadata rather than just rule names?
Yes. The filter_rule static method in your mode class receives the complete rule dictionary, allowing you to inspect fields like severity, tags, or description. Return True to retain the rule or False to exclude it based on any metadata criteria.
Where does the Agent validate operations against the ruleset?
The Agent class defined in ponytail/agents.py stores the assembled Ruleset instance in agent.ruleset. Before executing any action, the agent queries this object to verify the requested capability exists in the current filtered set, rejecting unauthorized operations with safety errors.
How do environment variables interact with CLI arguments for mode selection?
The resolution logic in ponytail/cli.py checks the --mode argument first. If omitted, it falls back to the PONYTAIL_MODE environment variable. If neither is set, it defaults to lazy-senior-dev. This hierarchy allows temporary CLI overrides of persistent environment configurations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →