# How Ponytail Persists and Retrieves Active Mode State: A Complete Guide

> Discover how Ponytail persists and retrieves active mode state. Learn about runtime variables, config json, and the _default_mode helper for seamless session management.

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

---

**Ponytail maintains active mode state in a runtime variable `_current_mode` for temporary session changes, while persisting defaults to [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) in the user configuration directory, reading values through the `_default_mode()` helper that checks environment variables before falling back to the JSON file.**

The DietrichGebert/ponytail repository implements a dual-layer storage strategy for managing operational modes (`off`, `lite`, `full`, `ultra`, `review`). This architecture separates transient runtime adjustments from permanent user preferences, ensuring both immediate responsiveness and cross-session persistence.

## Runtime Mode Storage

Ponytail stores transient mode changes in memory during the current process execution. This allows users to switch modes dynamically via the `/ponytail` command without affecting their saved defaults.

### The _current_mode Variable

The global variable `_current_mode` holds the mode value set during the active session. According to the source code in [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py), this variable is accessed directly whenever Ponytail determines which contextual instructions to inject before LLM calls ([source line 26-28](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L26-L28)).

When a user executes a command like `/ponytail lite`, the `_handle_mode_command()` function updates this variable. The value persists only until the process terminates, making it ideal for temporary workflow adjustments that should not survive application restarts.

## Persistent Configuration Storage

For settings that survive process restarts, Ponytail writes to a JSON configuration file in the user's config directory.

### config.json Location and Format

The persistent file resides at `$XDG_CONFIG_HOME/ponytail/config.json` on Linux systems, or `~/.config/ponytail/config.json` on other platforms. The directory resolution logic lives in `_config_dir()` within [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py) ([source line 44-49](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L44-L49)).

The JSON structure contains a single `"defaultMode"` field:

```json
{
  "defaultMode": "lite"
}

```

This file is created on first run and updated when users invoke persistence commands, ensuring their preferred mode remains active across separate application launches.

## How Ponytail Reads the Mode State

The system implements a cascading resolution strategy to determine the effective mode, prioritizing runtime changes over persistent configuration.

### The Mode Resolution Priority

When preparing context injection (e.g., in `_pre_llm_call`), Ponytail evaluates the active mode using the following precedence:

1. **Runtime variable** (`_current_mode`): Set via the `/ponytail` command in the current session
2. **Environment variable** (`PONYTAIL_DEFAULT_MODE`): System-level override for the default
3. **Configuration file** ([`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json)): User-persisted preference in the XDG directory
4. **Hardcoded fallback** (`DEFAULT_MODE`): Repository default when no other source exists

This logic ensures that temporary session changes always take precedence over saved settings, while environment variables allow system-wide overrides without modifying user files.

### The _default_mode() Function Implementation

The helper function `_default_mode()` in [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py) implements the cascading read logic for the default configuration ([source line 52-60](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L52-L60)):

```python
def _default_mode() -> str:
    env_mode = _normalize_config_mode(os.environ.get("PONYTAIL_DEFAULT_MODE"))
    if env_mode:
        return env_mode
    try:
        data = json.loads((_config_dir() / "config.json").read_text(encoding="utf-8"))
        file_mode = _normalize_config_mode(data.get("defaultMode"))
        if file_mode:
            return file_mode
    except Exception:
        pass
    return DEFAULT_MODE

```

The function first normalizes the environment variable value, then attempts to parse the JSON configuration, and finally falls back to the built-in default if neither source yields a valid mode.

### Context Injection Integration

Before each LLM call, Ponytail resolves the final mode using the expression found in [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py):

```python
mode = _current_mode or _default_mode()
context = build_injected_context(mode)

```

This single line demonstrates how the system bridges volatile runtime state with persistent configuration, using Python's `or` operator to implement the fallback chain.

## Working with Mode State Programmatically

You can interact with Ponytail's persistence layer directly in your own scripts.

### Reading the Current Effective Mode

To determine which mode is currently active in your code:

```python
from ponytail import _current_mode, _default_mode

def get_active_mode():
    # Prefer the runtime override, otherwise use the persisted default

    return _current_mode or _default_mode()

```

This mirrors the internal logic used by Ponytail's context injection system.

### Changing the Mode at Runtime

To simulate a user changing modes programmatically:

```python

# Simulate a user issuing "/ponytail lite"

from ponytail import _handle_mode_command

msg = _handle_mode_command("lite")
print(msg)  # → "Ponytail mode set to lite."

```

Note that this only affects `_current_mode` and does not modify [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json).

### Persisting a New Default Mode

To permanently save a mode preference across sessions:

```python
import json
from ponytail import _config_dir

def set_default_mode(mode: str):
    cfg_path = _config_dir() / "config.json"
    cfg_path.parent.mkdir(parents=True, exist_ok=True)
    cfg_path.write_text(json.dumps({"defaultMode": mode}), encoding="utf-8")

```

This creates or overwrites the configuration file in the user-specific directory, ensuring the setting persists after restarting the application.

## Summary

Ponytail's mode state persistence relies on a two-tier architecture that balances flexibility with durability:

- **Runtime storage** uses the `_current_mode` variable in [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py) for temporary session changes set via the `/ponytail` command
- **Persistent storage** writes to [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) in the XDG-compliant user configuration directory (resolved by `_config_dir()`)
- **Resolution priority** checks environment variables (`PONYTAIL_DEFAULT_MODE`) before falling back to the JSON file
- **Effective mode calculation** combines both layers using `mode = _current_mode or _default_mode()` before context injection

## Frequently Asked Questions

### What happens if config.json doesn't exist?

If the configuration file is missing or unreadable, the `_default_mode()` function catches the exception and returns the hardcoded `DEFAULT_MODE` constant. The application continues functioning without persistent storage until the user explicitly saves a preference.

### Can I override the mode without modifying the configuration file?

Yes. Set the `PONYTAIL_DEFAULT_MODE` environment variable to any valid mode (`off`, `lite`, `full`, `ultra`, `review`). The `_default_mode()` function checks this variable before attempting to read [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json), allowing system administrators to enforce defaults without touching user files.

### Where exactly is the config.json file located on my system?

The `_config_dir()` function in [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py) ([source line 44-49](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L44-L49)) resolves the path using the XDG Base Directory specification. On Linux, this respects `$XDG_CONFIG_HOME/ponytail/`. On macOS and Windows, it defaults to `~/.config/ponytail/`. You can verify the exact location by importing `_config_dir()` from the ponytail module and calling it directly.

### How does Ponytail validate mode values?

Both `_handle_mode_command()` and `_default_mode()` utilize the `_normalize_config_mode()` helper to validate inputs. This function ensures only the five recognized modes (`off`, `lite`, `full`, `ultra`, `review`) are accepted, returning `None` for invalid values and triggering the fallback chain.