# How Ponytail Handles Subagent Injection: Ruleset Propagation and Scoping

> Discover how Ponytail handles subagent injection by propagating its safety ruleset via the ponytail-subagent.js hook. Learn about ruleset propagation and scoping for secure agent communication.

- Repository: [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)
- Tags: deep-dive
- Published: 2026-08-29

---

**Ponytail injects its safety ruleset into subagents by running the [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) hook on every Agent tool startup, which writes the complete rule set to the `SubagentStart` output when the mode is active.**

Ponytail subagent injection ensures that safety guidelines propagate consistently from parent agents to any child agents spawned via the Agent tool. When operating in `lite`, `full`, or `ultra` mode, the system does not limit rule enforcement to the main session—it automatically extends these constraints to subagents through a dedicated hook mechanism. According to the DietrichGebert/ponytail source code, this injection is handled by [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js), which intercepts subagent initialization and conditionally applies the ruleset based on environment configuration.

## The Subagent Injection Hook

The core mechanism resides in [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js), which executes automatically whenever a subagent starts. This hook coordinates with [`ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-runtime.js) utilities and [`ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-instructions.js) to retrieve and deliver the appropriate ruleset.

### Activation and Mode Checking

Before injecting any content, the hook verifies that Ponytail is currently enabled. The `readMode()` function checks the active mode, and if the result is missing or explicitly set to `off`, the hook exits immediately without modifying the subagent environment【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L16-L21】. This check prevents unnecessary overhead when Ponytail is disabled.

### Core Injection Logic

When an active mode is detected, the hook retrieves the complete instruction set via `getPonytailInstructions(mode)` and writes it to the subagent's startup context using `writeHookOutput`. Specifically, it targets the `SubagentStart` output channel, ensuring the ruleset is present before the subagent begins processing【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L24-L27】. This approach guarantees that child agents inherit the same behavioral constraints as their parent without requiring manual configuration per subagent.

## Selective Subagent Injection via Regex Matching

Ponytail provides fine-grained control over which subagent types receive the ruleset through the `PONYTAIL_SUBAGENT_MATCHER` environment variable.

### Default Behavior: Universal Injection

If `PONYTAIL_SUBAGENT_MATCHER` is unset—the default configuration—the hook injects the ruleset into **every** subagent regardless of type【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L8-L11】. This ensures comprehensive coverage across all Agent tool invocations.

### Pattern-Based Scoping

When `PONYTAIL_SUBAGENT_MATCHER` is defined, the hook interprets the value as a **case-insensitive, unanchored regular expression**. During subagent startup, the hook reads the `agent_type` field from the stdin payload and applies the regex test before injecting【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L31-L39】. This allows selective targeting:

```bash

# Inject only into subagents whose type is exactly "general"

export PONYTAIL_SUBAGENT_MATCHER="^general$"
ponytail full

# Inject into subagents with type "explore" OR "general"

export PONYTAIL_SUBAGENT_MATCHER="explore|general"
ponytail full

```

## Robustness and Fail-Safe Mechanisms

The implementation includes several safeguards to maintain system stability and ensure rules are not silently dropped.

### Invalid Regex Handling

If the user provides an invalid regular expression in `PONYTAIL_SUBAGENT_MATCHER`, the catch block in the hook intercepts the error and sets the matcher to `null`, effectively falling back to the default behavior of injecting into every subagent【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L31-L39】.

### Fail-Open Safety

When the hook cannot parse the stdin payload or when the `agent_type` field is missing, the system **fails open**—meaning it proceeds with injection rather than risk omitting safety rules. This defensive design ensures that temporary JSON parsing errors or missing metadata do not leave subagents unprotected【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L50-L67】.

### Non-Blocking Execution

To prevent subagent startup delays, the hook implements timeout handlers and error boundaries that guarantee the process exits cleanly even if input reading or injection encounters issues【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L73-L78】.

## Implementation Example

The following simplified excerpt from [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js) demonstrates the complete injection flow:

```javascript
const { getPonytailInstructions } = require('./ponytail-instructions');
const { readMode, writeHookOutput } = require('./ponytail-runtime');

const mode = readMode();
if (!mode || mode === 'off') process.exit(0);

function inject() {
  writeHookOutput('SubagentStart', mode, getPonytailInstructions(mode));
}

// Regex matcher (optional)
let matcherRe = null;
if (process.env.PONYTAIL_SUBAGENT_MATCHER) {
  try { matcherRe = new RegExp(process.env.PONYTAIL_SUBAGENT_MATCHER, 'i'); }
  catch (_) { matcherRe = null; }
}

// No matcher → inject immediately
if (!matcherRe) { inject(); process.exit(0); }

// Matcher present → read agent_type from stdin, inject only on match
let input = '';
process.stdin.on('data', d => input += d);
process.stdin.on('end', () => {
  let agentType = '';
  try { agentType = JSON.parse(input).agent_type || ''; } catch (_) {}
  if (!agentType || matcherRe.test(agentType)) inject();
  process.exit(0);
});

```

## Summary

- **Automatic propagation**: The [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) hook runs on every subagent start in `lite`, `full`, or `ultra` mode, injecting the ruleset via `SubagentStart` output.
- **Configurable scoping**: Use `PONYTAIL_SUBAGENT_MATCHER` with case-insensitive regex to target specific `agent_type` values; leave unset to inject universally.
- **Defensive design**: Invalid regex patterns and JSON parsing errors trigger fail-open behavior, ensuring subagents receive rules rather than being skipped.
- **Non-blocking**: Timeout and error handlers prevent the hook from delaying subagent initialization.

## Frequently Asked Questions

### What triggers Ponytail subagent injection?

Ponytail subagent injection activates automatically when the system runs in any active mode (`lite`, `full`, or `ultra`). The [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) hook executes on every Agent tool startup, checking the current mode via `readMode()` before proceeding with injection.

### How do I limit Ponytail rules to specific subagent types?

Set the `PONYTAIL_SUBAGENT_MATCHER` environment variable to a regular expression matching your desired `agent_type` values. The regex is case-insensitive and unanchored by default. For example, `export PONYTAIL_SUBAGENT_MATCHER="coder|reviewer"` applies rules only to subagents with those types.

### What happens if the subagent metadata is corrupted or missing?

According to the source code in [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js)【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L50-L67】, if the hook cannot parse the stdin payload or find the `agent_type` field, it **fails open** and injects the ruleset anyway. This prevents security gaps due to transient data errors.

### Can the subagent hook block or slow down agent startup?

No. The implementation includes explicit timeout handlers and error catching that ensure the hook exits cleanly even if input reading fails or encounters unexpected errors【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-subagent.js#L73-L78】, preventing any delay in subagent initialization.