> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Policy Engine

> Script which runtime serves each command line; route scripts run on the workspace's evaluator runtime.

A workspace can hold several runtimes for the same command, with different
trade-offs. `monty` runs `python3` in-process, sandboxed, stdlib-only:
perfect for quick one-liners and pipe transforms, useless for a script that
imports `pandas`. A `docker` entry is the opposite: your container, your
installed packages, but every line pays the exec round-trip. The **route**
decides per line which one serves it:

```yaml theme={null}
mode: exec
runtimes:
  - monty # captures python3 by default: sandboxed, stdlib-only
  - name: docker
    captures: ["python3"] # your container, has pandas installed
    config:
      container: my-box
  - vfs
route: ./route.py
```

```python route.py theme={null}
# Jobs under /jobs need the container's packages; anything else
# stays on the fast in-process sandbox. The LAST EXPRESSION is
# the verdict.
stage = ctx["commands"][0]
in_jobs = any(p.startswith("/jobs/") for p in stage["paths"])
"docker" if in_jobs else "monty"
```

So `python3 /jobs/train.py --epochs 3` runs in the container, while
`cat data.csv | python3 -c "import sys; print(len(sys.stdin.read()))"`
stays on monty.

## Input: the line's context

The script sees one global, `ctx`, the parsed line before any routing.
This payload is captured from a live run of the config above, routing
`python3 /jobs/train.py --epochs 3`:

```json theme={null}
{
  "line": "python3 /jobs/train.py --epochs 3",
  "commands": [
    {
      "command": "python3",
      "words": ["python3", "/jobs/train.py", "--epochs", "3"],
      "builtin": true,
      "paths": ["/jobs/train.py"]
    }
  ],
  "command": "python3",
  "builtin": true,
  "cwd": "/",
  "env": {},
  "session_id": "019fb2fc-19a3-77cf-bbbe-c76069eed523",
  "agent_id": "",
  "mounts": ["/.bash_history/", "/data/", "/dev/", "/"]
}
```

* `commands`: one entry per pipeline stage (`cat x | python3 -` has two),
  each with its full words and its absolute-path operands.
* `command`, `builtin`: mirror the first stage. `builtin` means mirage has
  a builtin spec for the command (`python3` is a builtin that hands its
  code to a runtime), as opposed to an unknown name.
* `env`: the session environment (empty here, no `export` yet).
* `session_id`: the executing session's id. `agent_id` is empty until an
  agent identity is attached.
* `mounts`: the workspace's mount prefixes, including built-ins like
  `/dev/`.

The payload round-trips through JSON (`RouteContext.from_dict`), so you
can store one and replay a decision in tests.

## Output: the verdict

* **A runtime name** (`"docker"`): that entry serves every command it
  captures on this line.
* **`None`**: no opinion; the first capturer in the `runtimes` list order
  serves each command (here, `monty`).
* Anything else is a routing error; the line fails rather than guessing.

Besides the global `route`, each runtime entry can carry its own `script:`
with the same `ctx` input but a boolean verdict: am I willing to serve this
line? Unwilling entries step aside and the first willing capturer wins.

## What runs the scripts

`route.py` is never imported: the workspace evaluates its source on the
**policy engine**, the first entry in the `runtimes` list with the
evaluator capability. In the config above that is `monty`. A config with
route scripts but no evaluator entry fails at the first decision.

When you build the workspace in code rather than from a config file,
`route=` also accepts a plain function with the same decision contract,
sync or async. It is called directly, so no evaluator is involved:

```python theme={null}
from mirage.runtime.route import RouteContext

def route(ctx: RouteContext) -> str | None:
    stage = ctx.commands[0]
    in_jobs = any(p.startswith("/jobs/") for p in stage.paths)
    return "docker" if in_jobs else "monty"

ws = Workspace(mounts, runtimes=runtimes, route=route)
```

A per-entry `script=` likewise accepts `(ctx) -> bool` in code.

## Bring your own

The capability is open: inherit `EvaluatorMixin` (Python) or implement
`Evaluator` with the `EVALUATOR` brand (TypeScript) on your own runtime,
and it becomes eligible as the policy engine. `eval(code, inputs=...)`
returns the last expression's value; how the value travels is your
runtime's choice. The docker examples give a stock container the
capability by piping a harness to `python3 -`:
[python](https://github.com/strukto-ai/mirage/blob/main/examples/python/runtimes/docker/docker_eval.py),
[typescript](https://github.com/strukto-ai/mirage/blob/main/examples/typescript/runtimes/docker/docker_eval.ts).
