# How RTK's Tee System Facilitates Failure Recovery and Full-Output Retrieval

> RTK's tee system captures unfiltered command output for failure recovery and full retrieval. Access complete original logs for debugging with RTK's solution.

- Repository: [rtk-ai/rtk](https://github.com/rtk-ai/rtk)
- Tags: internals
- Published: 2026-04-24

---

**RTK's tee system captures raw, unfiltered command output to timestamped log files when failures occur or filters truncate data, then surfaces a retrievable file path hint that enables LLMs and users to access the complete original output for debugging.**

The `rtk-ai/rtk` repository implements a sophisticated tee subsystem that solves the critical problem of lost diagnostic information in AI-assisted command workflows. When token-saving filters compress or truncate command output, this system automatically persists full execution logs to disk, ensuring that no essential debugging data disappears during the filtering pipeline.

## Configuration and Operating Modes

The tee behavior is governed by `TeeConfig` in **[`src/core/config.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/config.rs)** (lines 41‑50), which exposes several tunable parameters:

- **`enabled`** – Global boolean switch that activates the entire subsystem.
- **`mode`** – Enum controlling when files are written: `failures` (only on non‑zero exit codes), `always` (every execution), or `never` (disabled entirely). Defaults to `failures`.
- **`max_files`** – Rotation limit keeping only the 20 most recent logs per directory.
- **`max_file_size`** – 1,048,576 byte cap per file; larger output truncates safely at UTF‑8 boundaries.
- **`directory`** – Optional custom path; otherwise defaults to `~/.local/share/rtk/tee/`.

The `TeeMode` enum itself is defined in **[`src/core/tee.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/tee.rs)** (lines 31‑39), providing the type safety for these configuration options.

## Decision Logic: When to Capture Output

The core decision function `should_tee` in **[`src/core/tee.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/tee.rs)** (lines 84‑100) applies a series of guards before writing any data:

1. **Global enable check** – Returns immediately if `config.enabled` is false.
2. **Mode evaluation** – `Never` aborts; `Failures` requires `exit_code != 0`; `Always` proceeds unconditionally.
3. **Minimum size threshold** – Outputs smaller than 500 bytes (`MIN_TEE_SIZE`) are ignored to prevent clutter.
4. **Directory resolution** – Confirms a valid tee directory exists via `get_tee_dir`, which checks the `RTK_TEE_DIR` environment variable, the config field, or falls back to the platform data directory.

```rust
fn should_tee(
    config: &TeeConfig,
    raw_len: usize,
    exit_code: i32,
    tee_dir: Option<PathBuf>,
) -> Option<PathBuf> {
    if !config.enabled { return None; }

    match config.mode {
        TeeMode::Never => return None,
        TeeMode::Failures => { if exit_code == 0 { return None; } }
        TeeMode::Always => {}
    }

    if raw_len < MIN_TEE_SIZE { return None; }

    tee_dir
}

```

## File Writing and Rotation Mechanisms

Once `should_tee` approves the operation, `write_tee_file` in **[`src/core/tee.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/tee.rs)** (lines 112‑155) handles the persistence:

- **Safe filename construction** – Generates `{epoch}_{command_slug}.log` where non‑alphanumeric characters in the slug convert to underscores and truncate to 40 characters.
- **UTF‑8 safe truncation** – Uses `char_indices` to split only at valid character boundaries when output exceeds `max_file_size`, appending a `--- truncated ... ---` notice.
- **Automatic rotation** – `cleanup_old_files` sorts existing logs by epoch prefix and deletes the oldest entries when the count exceeds `max_files`.

The system creates the directory tree recursively if missing, ensuring robust operation in fresh environments.

## User Hints and Recovery Workflow

The public entry point `tee_and_hint` in **[`src/core/tee.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/tee.rs)** (lines 188‑191) orchestrates the capture and user notification:

1. Checks for the `RTK_TEE=0` environment override.
2. Loads configuration and resolves the tee directory.
3. Executes the `should_tee` guard logic.
4. Writes the file and returns a formatted hint string.

```rust
pub fn tee_and_hint(raw: &str, command_slug: &str, exit_code: i32) -> Option<String> {
    // Environment override & config loading...
    let tee_dir = should_tee(&config.tee, raw.len(), exit_code, Some(tee_dir))?;
    write_tee_file(...).map(format_hint)
}

```

When RTK prints `[full output: ~/rtk/tee/1701234567_cargo_test.log]`, users or LLMs can read that file to obtain the unfiltered original, bypassing any compression or truncation that occurred in the main output stream.

## Forced Tees on Filter Truncation

Some filters (such as the AWS output processor) may truncate content while still returning a successful exit code. The `force_tee_hint` function in **[`src/core/tee.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/tee.rs)** (lines 199‑213) bypasses the `mode` check and always writes the file when `FilterResult.truncated` is true, provided the output meets the minimum size threshold. This ensures that large payloads intentionally clipped by filters remain recoverable even when the command technically succeeded.

## Integration Points

The tee system hooks into RTK's execution flow at three critical locations:

**Command Runner (**[`src/main.rs`](https://github.com/rtk-ai/rtk/blob/main/src/main.rs)**, lines 66‑78):** After a subprocess completes, if `!output.status.success()`, the system calls `tee_and_hint` with the merged stdout and stderr, then prints the hint after the filtered output.

**Filtered Runners (**[`src/core/runner.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/runner.rs)**, lines 9‑12):** When filtered commands return successfully but the filter forces a tee, `print_with_hint` appends the path to the filtered output.

**Streamed Mode (**[`src/core/runner.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/runner.rs)**, lines 25‑31):** Even during real‑time streaming, the system checks `opts.tee_label` and prints the hint after capture completes, ensuring streamed failures also leave a recoverable log.

## Environment Overrides

Two environment variables provide runtime control without editing configuration files:

- **`RTK_TEE=0`** – Disables teeing entirely for the current process, checked at the entry points of `tee_raw`, `tee_and_hint`, and `force_tee_hint`.
- **`RTK_TEE_DIR=/custom/path`** – Overrides the storage location, useful in CI/CD pipelines where the default `~/.local/share/rtk/tee/` may not be writable or persistent.

Example usage:

```bash
export RTK_TEE_DIR=/tmp/rtk_logs
rtk cargo test   # Failure logs write to /tmp/rtk_logs

```

## Summary

- RTK's tee system automatically persists raw command output to log files when configured conditions are met.
- The `should_tee` function in [`src/core/tee.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/tee.rs) gates writes based on mode, exit code, and minimum size thresholds.
- Files are safely truncated at UTF‑8 boundaries and automatically rotated to respect `max_files` limits.
- User hints provide direct paths to full output, enabling LLMs to reference complete logs even after aggressive filtering.
- Environment variables `RTK_TEE` and `RTK_TEE_DIR` offer flexible runtime control for CI pipelines and debugging sessions.

## Frequently Asked Questions

### Where does RTK store tee log files by default?

By default, RTK writes tee logs to `~/.local/share/rtk/tee/` on Linux and macOS, or the equivalent platform data directory on other systems. You can customize this path via the `directory` field in `TeeConfig` or by setting the `RTK_TEE_DIR` environment variable to an absolute path.

### How can I force RTK to save output even when a command succeeds?

Set the tee `mode` to `always` in your configuration, or use the `force_tee_hint` function programmatically when implementing custom filters. The `always` mode bypasses the exit code check in `should_tee`, writing a log file for every execution that meets the minimum size requirement.

### What happens when tee output exceeds the maximum file size?

When output exceeds the `max_file_size` limit (default 1,048,576 bytes), RTK truncates the content at the nearest valid UTF‑8 character boundary using `char_indices` and appends a `--- truncated ... ---` marker. This prevents file corruption while still preserving the initial actionable portion of large outputs.

### Can I completely disable the tee system for specific commands?

Yes. Set the environment variable `RTK_TEE=0` before running RTK, or configure `enabled: false` in the `TeeConfig` section of your settings file. The environment variable is checked early in the `tee_and_hint` execution path, ensuring zero overhead for that process.