> For the complete documentation index, see [llms.txt](https://docs.mithrl.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mithrl.com/guides/workflow-cli.md).

# Running and managing Inference

The mithrl CLI handles the complete inference lifecycle through the platform origin. Your bearer token or API key is sent only to the platform control-plane routes. File uploads and artifact downloads use time-limited object-store URLs, so the bytes do not transit the platform.

## 1. Sign in

```bash
mithrl login
```

Headless callers can activate an API key previously registered with `mithrl keys add`:

```bash
mithrl keys use --name ci-agent
```

## 2. Discover a workflow and its inputs

```bash
mithrl workflows
mithrl workflows --id upstream-regulator-analysis
```

The detail response includes `run_inputs`. Each entry names the key accepted by `mithrl run --input`; workflow inputs are reusable artifact `file_ref` values. An entry's `columns` names the header the uploaded file must have — for example `background_csv` requires a single `curie` column of canonical KG CURIEs, unlike `omics_csv`, whose identifiers are resolved and may be symbols.

## 3. Run with automatic staging

Prefix a local path with `@` to upload it before submission:

```bash
mithrl run upstream-regulator-analysis \
  --input omics_csv=@cohort.csv \
  --input background_csv=@background.csv \
  --label cohort-a
```

The `@` is required for a local file. A bare value is treated as an existing `file_ref`, so a path passed without it is rejected before submission with the corrected flag to use.

The CLI prints the run label and a shell-safe retry command before submission. If the connection drops after the platform accepts the run, use that exact command; it reuses the staged `file_ref` values and label, reproducing the idempotency key instead of creating duplicate work.

Use `--no-wait` to return immediately:

```bash
mithrl run upstream-regulator-analysis --input omics_csv=@cohort.csv --no-wait
mithrl status run_01EXAMPLE --watch
```

Polling is bounded so the command always terminates. The budget outlasts the presets' measured runtimes, but if a run is still active when it expires, `mithrl run` exits `3` (incomplete) rather than `0`: it prints its usual payload, with `data.poll_exhausted: true`, the `run_id`, and the `mithrl status ... --watch` command to reattach with. Re-running that resumes watching from wherever the run has reached. Exit `3` is not a failure — nothing needs re-submitting, and the run is still executing — but it is not success either, so a script chaining on `mithrl run ... && ...` correctly stops instead of processing results that do not exist yet. `--no-wait` asks only for the acceptance envelope, so it exits `0`.

`mithrl status <run_id> --watch`'s own watch is bounded the same way, but it always exits `0` — whether it reached a terminal state or its own bounded watch expired first — so do not use its exit code the way you can `mithrl run`'s: check `data.run.status` for terminal state instead, and re-run the same command to keep watching. And exit `0` is not a verdict on either command: a run that reaches `failed` is a *finished* run and still exits `0`, with the failure in `data.run.status` / `data.results`.

## 4. Reuse an uploaded file

Upload once when several runs share the same input:

```bash
mithrl upload cohort.csv
mithrl run upstream-regulator-analysis --input omics_csv=art_01EXAMPLE
```

If an upload is interrupted, its structured error includes an `upload_id` and resume command:

```bash
mithrl upload cohort.csv --resume up_01EXAMPLE
```

## 5. Manage runs

```bash
mithrl runs --status running --page 1 --per 25
mithrl cancel run_01EXAMPLE
mithrl rerun run_01EXAMPLE
mithrl retry run_01EXAMPLE
```

`rerun` forks a completed run with the same resolved configuration. `retry` is for a failed run whose failure is retryable. Both return a child run ID and a status command.

Each fork prints its own `--idempotency-key` before submitting. Running `rerun` twice forks twice, which is the intent; if the connection drops before a fork's response arrives, pass the printed key back instead of re-running the bare command, and the already-created child is returned rather than a duplicate:

```bash
mithrl rerun run_01EXAMPLE --idempotency-key cli-2f6c...
```

A reused key that already resolved says so on stderr, so a replay is never mistaken for new work.

A fork whose request hits a transient upstream failure (a 5xx, or a dropped connection) is retried automatically, up to three attempts with backoff. Every attempt sends the *same* key, so a fork the first attempt actually created is returned rather than duplicated. Anything else — a run that is still active, a failure that is not retryable, a run that does not exist — fails immediately, since a second identical request only repeats the same refusal.

Only the first attempt waits out the full multi-minute budget a healthy-but-long fork can need; a retry, which is replaying that same key against a decision the upstream has very likely already made, uses a much shorter timeout — so three attempts cost close to one long wait plus two short ones, not three long waits back to back. If the internal retry itself is what surfaces the already- created child (rather than a key you passed), the CLI says so plainly instead of warning that a key "was already used" — no key was reused; the retry just did its job.

`cancel` reports whether it cancelled anything: `cancellation` is `requested` when the run was still active, or `noop_terminal` when the run had already finished on its own and nothing was stopped. The status alone cannot say — cancellation is cooperative, so a cancel that landed often reports a still-running run.

## 6. Fetch findings and artifacts

```bash
mithrl run-results run_01EXAMPLE
mithrl run-results run_01EXAMPLE --download ./artifacts
```

Downloads stream to disk and never overwrite an existing file. If a presigned link expires, the CLI asks the platform for one fresh link and retries once. Artifacts that are transient or past retention are reported as unavailable rather than fabricated as empty files.

## 7. Validate a custom graph

Dry-run a JSON or YAML graph without creating a run:

```bash
mithrl validate graph.yaml
```

Validation returns the workflow service's complete warnings/errors envelope. A successful response contains `valid: true`; blocking graph or input problems exit with the CLI's structured error shape.
