# How the Agent Reach Doctor Command Identifies Active vs. Installed Backends

> Learn how Agent Reach doctor command identifies active vs installed backends by probing and selecting the first healthy candidate at runtime.

- Repository: [Pnant/Agent-Reach](https://github.com/Panniantong/Agent-Reach)
- Tags: how-to-guide
- Published: 2026-06-20

---

**The Agent Reach `doctor` command distinguishes installed backends—those statically declared in a channel's `backends` attribute—from the active backend, which is determined at runtime by probing each candidate and selecting the first one reporting a healthy status of `ok` or `warn`.**

The `doctor` command in the **Panniantong/Agent-Reach** repository serves as a comprehensive health checker that surveys every platform channel to report system readiness. This CLI tool differentiates between backend tools that are merely present on the system versus those that are actually functional and ready for use, storing these distinctions in specific class attributes and runtime properties.

## Understanding the Distinction: Installed vs. Active Backends

The `doctor` command reports two distinct concepts for each platform channel. Understanding the difference is critical for interpreting health check results correctly.

### Installed Backends (Static Discovery)

**Installed backends** represent all candidate tools configured for a specific channel. These are discovered through the static `backends` class attribute defined in each channel implementation.

In [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py), the base `Channel` class defines this attribute as a list of strings. Concrete channels like `TwitterChannel` populate this list with supported tools (e.g., `["twitter-cli", "OpenCLI", "bird CLI (legacy)"]`). The `doctor` command reads this list directly from the class definition without executing any system probes, making it a lightweight, compile-time enumeration of supported options.

### Active Backends (Runtime Detection)

**The active backend** is the single tool actually usable on the host at the moment of the check. Unlike the static list, this is determined dynamically through runtime probing.

Each channel's `check()` method implements platform-specific logic to test each candidate backend. The method probes binaries, checks API accessibility, or validates configurations. The first candidate returning a healthy status—either `ok` or `warn`—is stored in the instance attribute `self.active_backend`. This value is then extracted by the doctor module via `getattr(ch, "active_backend", None)` after the check completes.

## How Installed Backends Are Discovered Through Channel Registration

The discovery process begins in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py), where the `get_all_channels()` function returns a registry of all available channel instances. The `doctor.check_all` function iterates over this registry to survey the entire platform ecosystem.

For each channel instance, the doctor accesses the `backends` attribute directly. This static list requires no execution of external commands or system calls—it is simply a property of the class definition. For example, a channel supporting multiple Twitter clients would expose:

```python
class TwitterChannel(BaseChannel):
    backends = ["twitter-cli", "OpenCLI", "bird CLI (legacy)"]

```

This list represents the complete universe of tools the channel knows how to interact with, regardless of whether they are actually installed on the current machine.

## Runtime Probing: How the Active Backend Is Selected

While installed backends are declared statically, identifying the active backend requires execution. The selection logic resides in each channel's `check()` method, typically implemented in files like [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py).

### The Probing Hierarchy

The `check()` method iterates through `self.ordered_backends(config)` and probes each candidate using `probe_command()` (or equivalent platform-specific utilities). Each probe returns one of three outcomes:

- **missing**: The binary is not found on the PATH. The candidate is ignored and not considered installed.
- **broken/timeout**: The binary exists but cannot execute properly. The candidate is recorded but never selected as active.
- **ok/warn**: The binary runs and reports health status. These candidates become eligible for active status.

The method collects all non-missing candidates into a `findings` list, then implements a priority selection:

```python
def check(self, config=None):
    self.active_backend = None
    findings = []
    
    for backend in self.ordered_backends(config):
        if backend == "twitter-cli":
            result = self._check_twitter_cli()
        elif backend == "OpenCLI":
            result = self._check_opencli()
        # ... additional backends

        
        if result is not None:  # Filter out "missing"

            findings.append((backend, *result))
    
    # Select first "ok", otherwise first "warn"

    for wanted in ("ok", "warn"):
        for backend, status, message in findings:
            if status == wanted:
                self.active_backend = backend
                return status, message

```

### Priority of Selection

The algorithm prioritizes stability. It searches for the first `ok` status across all candidates. If no backend reports `ok`, it falls back to the first `warn` status. Backends reporting `broken` or `timeout` are never selected as active, even if they are the only options available.

## The Doctor Command Execution Flow

The orchestration logic in [`agent_reach/doctor.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/doctor.py) ties these components together into a cohesive health report.

### Step-by-Step Workflow

1. **Collect channels**: `check_all` calls `get_all_channels()` to retrieve the registry from [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py).

2. **Execute checks**: For every channel `ch`, the system calls `ch.check(config)`, which returns a tuple `(status, message)` and populates `ch.active_backend` as a side effect.

3. **Capture active backend**: Immediately after the check, the doctor reads `getattr(ch, "active_backend", None)` to retrieve the runtime selection.

4. **Build result dictionary**: The system constructs a dictionary containing the channel name, status, description, tier, the complete list of `backends`, and the detected `active_backend`.

5. **Render report**: The `format_report` function displays the active backend only when a channel has multiple candidates and a healthy active one was found.

### Running the Doctor Command

Execute the health check from your terminal:

```bash
python -m agent_reach.cli doctor

```

Sample output indicates both the available backends and the currently active one:

```text
✅ Twitter/X — Twitter CLI fully available (search, read tweets...) (current backend: twitter-cli)

```

In this example, multiple backends may be installed (`twitter-cli`, `OpenCLI`, `bird CLI`), but `twitter-cli` is designated as active because it was the first to report an `ok` status during probing.

## Summary

- **Installed backends** are statically defined in the `backends` class attribute of each channel in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py) and require no runtime detection.
- **Active backends** are determined at runtime by each channel's `check()` method, which probes candidates and selects the first healthy one (`ok` or `warn`), storing the result in `self.active_backend`.
- The `doctor` command in [`agent_reach/doctor.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/doctor.py) orchestrates this process by iterating the channel registry, executing checks, and reading the `active_backend` attribute to generate health reports.
- Probing logic distinguishes between missing binaries (not installed), broken binaries (installed but unusable), and healthy binaries (active candidates).

## Frequently Asked Questions

### What determines if a backend is considered "installed" versus "missing"?

A backend is considered **installed** if its binary exists on the system PATH and the `probe_command` function in [`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py) returns a status other than `missing`. If the executable cannot be found, the backend is excluded from the findings list entirely, even though it remains in the static `backends` declaration.

### Can a channel have multiple active backends simultaneously?

No. The architecture enforces a single active backend per channel instance. The `check()` method sets `self.active_backend` to a single string value representing the first candidate with an `ok` or `warn` status. If multiple backends are healthy, only the first one in the `ordered_backends` list becomes active.

### How does the doctor command handle backends that return a "warn" status?

Backends reporting `warn` are considered functional but potentially degraded. The selection algorithm in `check()` prioritizes `ok` status over `warn`, but if no `ok` backends exist, the first `warn` backend becomes the active selection. The doctor report displays the `warn` status to alert users of potential issues while still allowing the channel to operate.

### Where should I implement custom probing logic for a new channel?

Custom probing logic belongs in the `check()` method of your channel class, typically located in [`agent_reach/channels/your_channel.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/your_channel.py). Inherit from `BaseChannel` in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py), define your `backends` list, and override `check()` to implement platform-specific probing using `probe_command()` or direct API calls. Ensure you set `self.active_backend` before returning the status tuple.