# `plato pm start from-template`

Launch a Chronos session from a **local JSON launch template** — no Chronos upload required. Designed for scripting "run this same thing against N sims" without changing the SDK.

## When to use it

- You have a repeatable per-sim task (screenshot extraction, QA smoke check, catalog metadata backfill) and want to fan it out across a list of sims.
- You don't want to go through the `plato pm experiment push` upload flow every time you iterate on the prompt or verify logic.
- You're fine running the template verbatim from disk — what you commit to the repo is what gets launched.

The two templates that ship with the SDK today and use this path:

| Template | Purpose |
|---|---|
| [`extract-screenshots-launch.json`](../../templates/extract-screenshots-launch.json) | Agent captures 4 full-page screenshots of a sim and the verifier uploads them to the sim's catalog. |
| [`sim-checker-launch.json`](../../templates/sim-checker-launch.json) | Agent runs a 5-phase QA pass (loads, walks nav, makes 2–3 mutations, reads audit count, scores). |

## Synopsis

```
plato pm start from-template <template-path> <sims...> [options]
```

### Arguments

- `<template-path>` — path to a local JSON launch template (e.g. `templates/extract-screenshots-launch.json`).
- `<sims...>` — one or more of:
  - simulator name (e.g. `bookstack`) — artifact resolved from the sim's current `data_artifact_id` / `base_artifact_id`
  - bare artifact UUID (`b07fcb30-6af9-4e28-bef3-1aa6764b93c9`) — used directly, skips sim lookup
  - colon notation (`bookstack:b07fcb30-...`) — explicit artifact, no API lookup

### Options

| Flag | Effect |
|---|---|
| `--base` | Use `base_artifact_id` (pre-datagen snapshot). Mutually exclusive with `--data`. |
| `--data` | Use `data_artifact_id` (post-datagen snapshot). Default already falls back to base if data is missing. |
| `-a, --artifact-id <uuid>` | Explicit artifact UUID — requires exactly one simulator name. Mutually exclusive with `--unified` and with a bare-UUID positional. |
| `--unified` | Launch **one** session with all sims attached as envs (one tab per sim). Template must be written to handle multiple envs — see Unified mode below. |

## Examples

```bash
# One session per sim, post-datagen snapshot
plato pm start from-template \
  python-sdk/templates/extract-screenshots-launch.json \
  bookstack mattermost docmost --data

# Pre-datagen snapshot
plato pm start from-template \
  ./extract-screenshots-launch.json aureus --base

# Explicit artifact, colon notation
plato pm start from-template \
  ./extract-screenshots-launch.json bookstack:1b642f11-... 

# Bare artifact UUID — no sim lookup
plato pm start from-template \
  ./extract-screenshots-launch.json b07fcb30-6af9-4e28-bef3-1aa6764b93c9

# Unified session (template must reference config.sim_names)
plato pm start from-template \
  ./my-unified-template.json sim1 sim2 sim3 --unified
```

## Template shape

Every template has this skeleton — see `extract-screenshots-launch.json` for the minimal case and `sim-checker-launch.json` for a multi-step example.

```jsonc
{
  "world": {
    "package": "plato-world-structured-execution:latest",
    "runtime": { "type": "vm", "vm": { "cpus": 2, "memory": 4096, "disk": 20480, "timeout": 3600 } },
    "config": {
      "agent": {
        "package": "claude-code",
        "config": {
          "model_name": "anthropic/claude-haiku-4-5-20251001",
          "max_turns": 120,
          "anthropic_api_key": "{anthropic_api_key}"
        }
      },
      "anthropic_api_key": "{anthropic_api_key}",
      "plato_api_key":     "{plato_api_key}",
      "anchor_api_key":    "{anchor_api_key}",
      "mcps": [],
      "envs": [],
      "steps": [
        {
          "name": "do_the_thing",
          "max_attempts": 2,
          "instruction": "Agent prompt (placeholders like {sim_name} are rendered at launch)",
          "verify": [
            "builtin_verifier file_exists result.json",
            "jq -e '.ok == true' result.json > /dev/null || (echo FAIL && exit 1)"
          ]
        }
      ],
      "sim_name": "{sim_name}"
    }
  },
  "tags": ["my-task"],
  "mcps_required": ["browser"]
}
```

### `mcps_required` (top-level)

List of MCPs to attach to each env the template runs against. The SDK expands each entry into the right MCP config at launch time:

| Entry | What it expands to |
|---|---|
| `"browser"` | `{"type": "browser", "name": "browser", "backend": "browserbase"}` — Playwright via Browserbase. See [Browser backend selection](#browser-backend-selection) below. |
| `"vm"` | `{"type": "vm", "name": "vm"}` — shell/file access to the sim VM. |
| `"db"` | Fetches the sim's `db_config` via `GET /api/v1/simulator/{artifact_id}/db_config` and expands each returned DB (multi-DB sims become multiple entries). |
| `"functions"` | `{"type": "functions", "name": "functions", "session_id": "run-{sim_name}", "service": "{sim_name}"}`. |

Unknown values raise `Unsupported MCPs in mcps_required`.

### Browser backend selection

The world supports two browser backends — `anchor` and `browserbase` — selected per-MCP via `BrowserMcpConfig.backend`. `from-template` always hardcodes `"backend": "browserbase"` when expanding the `"browser"` entry, because that's the backend these workflows need. The template's `mcps_required` is a list of MCP names only — it doesn't carry a backend field, and there's no CLI flag to override. To change it, edit `_build_mcps_for_sim` in [`python-sdk/plato/cli/pm.py`](../../plato/cli/pm.py).

Other launchers (`plato pm start env`, `plato pm start data`, `plato pm start checker` via the Chronos experiment path) use the world default, which is `anchor`.

### Placeholders rendered at launch

Any `{key}` token in a step's `instruction` OR `verify` shell gets replaced. Available keys:

| Placeholder | Value |
|---|---|
| `{sim_name}` | Simulator alias. **Not** substituted in `--unified` mode (there are multiple sims). |
| `{artifact_id}` | Artifact UUID attached to the env. Not substituted in `--unified`. |
| `{plato_api_key}` | `PLATO_DATAGEN_API_KEY` from env. Use for verifier HTTP calls against the Plato app API. |
| `{plato_base_url}` | `https://plato.so` (or `$PLATO_BASE_URL`), with `/api` suffix stripped. |
| `{workspace}` | `/workspace`. |
| `{mutation_threshold}` | `config.mutation_threshold` from the template, default `40` — used by sim-checker's scoring step. |

Rendering happens per-step: open the template JSON, look at `world.config.steps[].instruction` and `world.config.steps[].verify[]` — any `{sim_name}` / `{plato_api_key}` / etc. in those strings gets substituted before the session is POSTed to Chronos.

### Unified mode (`--unified`)

One session, N sims attached as parallel envs. Differences vs single-sim mode:

- `config.envs` gets one entry per sim; each env has its own full MCP stack from `mcps_required`.
- `config.sim_names` (plural) gets populated; `config.sim_name` is set to the first sim for backward-compat with templates that still reference the singular.
- **`{sim_name}` and `{artifact_id}` placeholders are NOT substituted** — there's no single value. Templates designed for unified should read `config.sim_names` from within the world instead of relying on placeholder substitution.

## What gets injected at launch

On top of what your JSON declares, the SDK fills in:

- `agent.config.{anthropic_api_key | claude_oauth_credentials}` from Claude OAuth keychain or `ANTHROPIC_API_KEY`.
- `plato_api_key` = `PLATO_DATAGEN_API_KEY`.
- `anchor_api_key` / `browserbase_api_key` from env — both are written to `os.environ` inside the world so the browser backend can read them.
- `envs[]` — built from the sims you listed (artifact + alias + MCPs).
- `sim_name` / `artifact_id` / `sim_names` on `world.config` as described above.
- `tags` — each sim name is appended.

## Interactive prompt

Before launching, the CLI prints the resolved sims + artifact source and confirms:

```
Will launch extract-screenshots-launch.json (one session per 3 simulator(s)):
  bookstack — artifact 1b642f11... (data_artifact_id)
  mattermost — artifact f49c22b3... (data_artifact_id)
  aimeos — artifact 52a9cd1e... (data_artifact_id)

Proceed? [Y/n]:
```

To auto-confirm from a script: `yes y | plato pm start from-template ...`

## See also

- [How-to: creating a repeatable sim task](../howto/repeatable-sim-task.md)
- [`plato pm open`](./pm-open.md) — interactive Playwright session for manually poking at a sim
- [`plato pm start checker`](./pm.md) — the original Chronos-experiment-backed sim-checker launcher
