# How Ponytail Generates and Filters Rulesets for Different Agent Modes

> Learn how Ponytail generates and filters rulesets for different agent modes. Discover how mode-specific transformations customize agent capabilities for optimal performance.

- Repository: [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)
- Tags: how-to-guide
- Published: 2026-08-30

---

**Ponytail constructs agent rulesets by loading a base capability catalog from [`ponytail/ruleset.py`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail/ruleset.py), then applies mode-specific transformations defined in [`ponytail/modes.py`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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.

```python

# 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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail/cli.py) resolves the mode name through the `MODE_REGISTRY` before constructing the agent.

```bash

# Run with the debug mode (allows broader system access)

ponytail run --mode debug

```

```python

# 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`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail/modes.py). Implementers override the three filtering mechanisms to create specialized safety profiles.

```python

# 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.

```python
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.py`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail/ruleset.py) containing all possible agent capabilities.
- Mode configurations in [`ponytail/modes.py`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail/modes.py) specify additions, removals, and filter predicates.
- The `build_ruleset(mode)` function composes the final ruleset by applying mode transformations sequentially.
- Runtime selection occurs via the `--mode` CLI flag or `PONYTAIL_MODE` environment variable.
- Custom modes inherit from the `Mode` base class and implement `add_rules`, `remove_rules`, and `filter_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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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.