Use Understudy Gateway
Use this worker when the developer wants to run an application workload through
Understudy-managed inference or needs the CLI to execute a durable command while
the agent monitors status and artifacts.
The local evidence loop does not require auth. Route here only when the developer
explicitly asks for Understudy inference, gateway routing, project/key
management, workload route configuration, hosted execution, or authenticated
gateway routing.
Choosing frontier access? When an onboarding step, installer, or
local-vs-frontier comparison needs a remote frontier model, run the decision in
references/frontier-keys.md first. Default to
the Understudy managed catalog when the requested model is available there; use
BYO shell/.env keys only for unsupported models, provider-specific account
needs, or an explicit developer preference. It keeps secrets local, asks before
reading .env values, and records the choice without printing keys.
Safety Gates
Do not ask the developer to paste an API key. Use the CLI registration flow and
let the CLI store credentials outside the repo.
Do not print, commit, or write sk_* values into artifacts. understudy run
injects UNDERSTUDY_API_KEY, UNDERSTUDY_GATEWAY_URL, and the non-secret
UNDERSTUDY_ORG_ID when known only into the child process environment.
The developer's action that launches a named bounded workflow authorizes its
declared provider calls, uploads, hosted jobs, evaluation, receipts, and
cleanup. Do not pause for another confirmation between those phases. Ask again
only before expanding the displayed data, destination, spend, retention,
download, credential, or production-impact envelope.
Always stream gateway inference
Every inference request sent to the Understudy gateway (/v1/messages or
/v1/chat/completions) must set stream: true. This is not a style
preference: the gateway sits behind an edge that cuts any origin response
producing no first byte within ~125 seconds and returns a 524 to the client.
A non-streaming request holds the response open for the model's full
generation time, so a slow generation can cross that limit and fail — and a
524 carries no usage block, so the request's tokens cannot be metered. With
stream: true the upstream returns headers and SSE framing within seconds,
so the first-byte timeout can never fire regardless of generation length.
If the caller needs the full response as a single object, still stream — then
aggregate locally. Do not "simplify" a streaming call back to stream: false.
For OpenAI-shape streaming requests the gateway injects
stream_options: { include_usage: true } upstream itself, so the final SSE
chunk carries usage without the caller setting anything. Aggregation patterns
per client (Anthropic SDK, OpenAI SDK, raw fetch/SSE) are in
reference.md → "Always-stream rule".
Resolve CLI
Prefer the installed understudy binary. If it is unavailable inside a repo
checkout, run through the package script:
npm run build
node dist/bin.js status --json
Flow
Check whether auth is already configured:
understudy status --json
If not signed in, register/sign in with the two-phase email-code flow:
understudy login --email <developer-email> # emails a one-time code, then exits
understudy login --code <one-time-code> # completes the sign-in
In a non-TTY shell (an agent's shell) the first command sends the code and
exits with instructions; the pending claim is saved under
~/.understudy/login-pending.json, so no interactive prompt has to be held
open — no tmux or expect tricks. In an interactive terminal the same command
prompts for the code inline instead. Codes expire after about 10 minutes;
rerun --email to send a fresh one.
This creates or finds the developer's Understudy account, retrieves an API
key through the CLI, and stores it outside the repo. The key is used for
authenticated gateway inference, project/key management, and remote model
routes. Do not ask the developer to paste the key.
Ask the developer to read the one-time code from their inbox. An agent with
an approved native email connector may instead search narrowly for the fresh
Understudy sign-in email and pass the code to understudy login --code. Read
only that email, and do not persist the code anywhere else.
Confirm project/key readiness:
understudy projects list --json
understudy keys list --json
For frontier-vs-Understudy comparison, list public model IDs first. If the
account is keyless for frontier providers, prefer the keyless catalog
sweep recipe below: clear the workload route and vary the request-body
model. Use traffic-split A/B only after confirming the non-routed
passthrough share has a managed provider credential or BYO key.
Run the local command through the gateway wrapper only after approval:
understudy run -- <local command>
Run this as a background task — do not block the agent thread on it.
Eval runs can take minutes to hours depending on row count and model latency.
Send it to the background immediately and move on to other work. See
../../docs/background-ops.md for the
shell patterns and how to tail the log and check the exit code.
Monitor the command output and local artifacts by tailing the log rather than
blocking the agent. While the run is in flight, advance the loop steps that
don't depend on its output: cost-model the candidate models, pull benchmark
context for the comparison, and scaffold the evidence artifacts
(../understudy/reference.md § "Understudy
Agent Improvement Report"). Surface a notification when the run completes;
do not silently continue. For route work, route back to
../optimize-workload/SKILL.md once the run
has produced candidate/proof evidence. See
../../docs/background-ops.md for the exact
tail/poll and exit-code patterns.
Keyless catalog sweep
Use this recipe for the fastest frontier/open-weight comparison when the
developer does not have, or does not want to use, provider keys. The same
Understudy key can request supported Anthropic, OpenAI, and open-weight catalog
models by public id.
Discover public model options:
understudy models list --json
Ensure the eval workload exists on the gateway, then has no route
configured. A route dialed to 0% still counts as configured and prevents
request-time catalog resolution. Register from the local card if needed,
then clear:
understudy workloads create --project-id <project-id> \
--from-card .understudy/workload-discovery/workload-card.json
understudy workloads route <workload-name> --project-id <project-id> --clear
Use the card's workload_name when set, otherwise its workload_id, as
<workload-name>. Skip create if understudy workloads list already shows
that name.
Run the frozen eval once per catalog model by changing only the request-body
model value. Send x-understudy-project and x-understudy-workload on
every request so the run does not accidentally use the org default workload.
Record the legibility headers for every row: x-understudy-mode,
x-understudy-route, and x-understudy-effective-model. Treat rows where the
requested model and effective model disagree as invalid for model comparison.
Unknown ids for a keyless managed org return a clean catalog-miss 404 with
available alternatives. Do not expect arbitrary frontier names to passthrough
unless the org has its own managed provider key or the request supplies BYO
credentials.
Bound the run by row count, max tokens, and wall-clock time. New accounts get
prepaid credit and async suspension, not a synchronous per-request spend
reservation.
A/B model routing
Use this recipe to A/B a chosen public model against passthrough while an eval
runs through the gateway. A typical consumer is
../optimize-agentic-workload/SKILL.md,
comparing a workload's quality and cost across the split.
Discover public model options (public model IDs only; no supplier detail):
understudy models list --json
The same catalog is enumerable in-band via OpenAI-compatible
GET {gateway}/v1/models with the sk_* key — OpenAI(...).models.list()
works — useful from a harness that already speaks the OpenAI SDK.
The list is the remote ladder. It should include larger Gemma-family routes
when enabled for the account, so the same API key can graduate a workload from
the local Gemma 4 E2B first rung to larger Gemma variants or remote/hybrid
routes without changing application code.
Eval shortcut — request-time catalog resolution. On a workload with no
route configured (never routed, or explicitly --cleared — a route dialed
to 0% still counts as configured and stays passthrough), a request for any
active catalog id ("model": "glm-5.1", "model": "gpt-5.5", or another
listed id) is served from managed supply with no per-model setup. For a
keyless managed org, unknown ids return a clean catalog-miss 404 with
available alternatives instead of falling through to unpriced provider
passthrough. So a model sweep needs exactly one unrouted workload and can
iterate over listed catalog ids in the request body.
Selecting the workload per request. The gateway resolves which workload's
route serves a request from two optional headers: x-understudy-project
(project slug) and x-understudy-workload (workload name). Absent headers
fall back to the org's default workload — whose route may rewrite the model.
A bench harness should always send both headers and never assume
requested == served: read the canonical legibility headers the gateway
returns on every response — x-understudy-mode (managed/byo),
x-understudy-route (primary / understudy / fallback), and
x-understudy-effective-model (the public id that actually served) — and
record the effective model, falling back to the response-body model field
only if the headers are absent. Exclude runs where the requested and
effective models disagree.
Register the local workload card on the gateway if it is not already there.
A local workload-card.json is only on disk until create runs — routing a
card id that was never registered returns not-found.
understudy workloads create --project-id <project-id> \
--from-card .understudy/workload-discovery/workload-card.json
Skip create when understudy workloads list already shows the card's
workload_name (or workload_id when name is null).
Route a workload to a model at a traffic percentage — a per-request split
where that share goes to the routed model and the rest stays on passthrough.
Pick a bounded share (e.g. 30%) to keep the comparison small. Use the same
name create used (card workload_name or workload_id):
understudy workloads route <workload-name> --project-id <project-id> --model-id gemma-4-12b --traffic-pct 30
Clearing the route (--clear in place of the model/traffic flags) returns the
workload to full passthrough. The hosted split semantics are documented at
docs.understudylabs.com/concepts/routing.
Run the eval through the gateway so the routed model serves its share. Any
local command works; an eval harness like a verifiers vf-eval run is typical.
understudy run -- vf-eval <eval-id>
Prerequisite for a frontier comparison. For the split to compare the routed
model against a frontier model, the non-routed (passthrough) share must have a
configured managed provider key or BYO key; the managed catalog only resolves
on no-route workloads. Without passthrough credentials, those non-routed
requests error. For keyless accounts, use the keyless catalog sweep recipe
first, then route only after choosing a candidate.
Diagnostics
When the developer asks whether the gateway is causing errors, wants to check
provider health, or asks "is this us?", route to
../check-routing-health/SKILL.md. That
worker calls the read-only reporting endpoints (routing-status, provider-health,
compact status) and interprets the results. The full endpoint reference is at
docs.understudylabs.com/reference/control-plane/reporting.
Output Standard
End with:
- auth status without revealing secrets;
- project/key readiness;
- model route status when configured;
- command run or blocked;
- whether provider calls or hosted execution were approved;
- local artifact path or next CLI command to monitor.
References
Domain depth in reference.md:
- Trace capture — gateway or local trace capture without changing the app
interface; capture calls/prompts/responses/tool calls/latency/errors/tokens/
metadata; redact; skip upload in local-only / restricted (ZDR) modes; produce a
trace inventory (defer call-site discovery to
../capture-evidence/SKILL.md).
- Deploy and compare — reproducible baseline, smallest coherent route/config
change that solves the measured cause via the workloads API (or a local
understudy.yaml), rollback, comparison evals, before/after metrics, surfaced
regressions.
For route selection and the fresh-pricing rule, see
../understudy/reference.md; for measured claims,
../optimize-workload/SKILL.md.
1---2name: use-understudy-gateway3description: Use when a developer wants to run inference or evals through the Understudy gateway — "route my app through Understudy", "set up my account and keys", "A/B a model on part of my traffic" — or must choose between local provider keys and the gateway route ("should I use my OpenAI key or the gateway"). Handles login, projects/keys, model routing, and runs.4---56# Use Understudy Gateway78Use this worker when the developer wants to run an application workload through9Understudy-managed inference or needs the CLI to execute a durable command while10the agent monitors status and artifacts.1112The local evidence loop does not require auth. Route here only when the developer13explicitly asks for Understudy inference, gateway routing, project/key14management, workload route configuration, hosted execution, or authenticated15gateway routing.1617**Choosing frontier access?** When an onboarding step, installer, or18local-vs-frontier comparison needs a remote frontier model, run the decision in19[`references/frontier-keys.md`](references/frontier-keys.md) first. Default to20the Understudy managed catalog when the requested model is available there; use21BYO shell/`.env` keys only for unsupported models, provider-specific account22needs, or an explicit developer preference. It keeps secrets local, asks before23reading `.env` values, and records the choice without printing keys.2425## Safety Gates2627Do not ask the developer to paste an API key. Use the CLI registration flow and28let the CLI store credentials outside the repo.2930Do not print, commit, or write `sk_*` values into artifacts. `understudy run`31injects `UNDERSTUDY_API_KEY`, `UNDERSTUDY_GATEWAY_URL`, and the non-secret32`UNDERSTUDY_ORG_ID` when known only into the child process environment.3334The developer's action that launches a named bounded workflow authorizes its35declared provider calls, uploads, hosted jobs, evaluation, receipts, and36cleanup. Do not pause for another confirmation between those phases. Ask again37only before expanding the displayed data, destination, spend, retention,38download, credential, or production-impact envelope.3940## Always stream gateway inference4142Every inference request sent to the Understudy gateway (`/v1/messages` or43`/v1/chat/completions`) must set `stream: true`. This is not a style44preference: the gateway sits behind an edge that cuts any origin response45producing no first byte within ~125 seconds and returns a 524 to the client.46A non-streaming request holds the response open for the model's full47generation time, so a slow generation can cross that limit and fail — and a48524 carries no usage block, so the request's tokens cannot be metered. With49`stream: true` the upstream returns headers and SSE framing within seconds,50so the first-byte timeout can never fire regardless of generation length.5152If the caller needs the full response as a single object, still stream — then53aggregate locally. Do not "simplify" a streaming call back to `stream: false`.54For OpenAI-shape streaming requests the gateway injects55`stream_options: { include_usage: true }` upstream itself, so the final SSE56chunk carries usage without the caller setting anything. Aggregation patterns57per client (Anthropic SDK, OpenAI SDK, raw fetch/SSE) are in58[`reference.md`](reference.md) → "Always-stream rule".5960## Resolve CLI6162Prefer the installed `understudy` binary. If it is unavailable inside a repo63checkout, run through the package script:6465```sh66npm run build67node dist/bin.js status --json68```6970## Flow71721. Check whether auth is already configured:7374 ```sh75 understudy status --json76 ```77782. If not signed in, register/sign in with the two-phase email-code flow:7980 ```sh81 understudy login --email <developer-email> # emails a one-time code, then exits82 understudy login --code <one-time-code> # completes the sign-in83 ```8485 In a non-TTY shell (an agent's shell) the first command sends the code and86 exits with instructions; the pending claim is saved under87 `~/.understudy/login-pending.json`, so no interactive prompt has to be held88 open — no tmux or expect tricks. In an interactive terminal the same command89 prompts for the code inline instead. Codes expire after about 10 minutes;90 rerun `--email` to send a fresh one.9192 This creates or finds the developer's Understudy account, retrieves an API93 key through the CLI, and stores it outside the repo. The key is used for94 authenticated gateway inference, project/key management, and remote model95 routes. Do not ask the developer to paste the key.9697 Ask the developer to read the one-time code from their inbox. An agent with98 an approved native email connector may instead search narrowly for the fresh99 Understudy sign-in email and pass the code to `understudy login --code`. Read100 only that email, and do not persist the code anywhere else.1011023. Confirm project/key readiness:103104 ```sh105 understudy projects list --json106 understudy keys list --json107 ```1081094. For frontier-vs-Understudy comparison, list public model IDs first. If the110 account is keyless for frontier providers, prefer the **keyless catalog111 sweep** recipe below: clear the workload route and vary the request-body112 `model`. Use traffic-split A/B only after confirming the non-routed113 passthrough share has a managed provider credential or BYO key.1141155. Run the local command through the gateway wrapper only after approval:116117 ```sh118 understudy run -- <local command>119 ```120121 **Run this as a background task — do not block the agent thread on it.**122 Eval runs can take minutes to hours depending on row count and model latency.123 Send it to the background immediately and move on to other work. See124 [`../../docs/background-ops.md`](../../docs/background-ops.md) for the125 shell patterns and how to tail the log and check the exit code.1261276. Monitor the command output and local artifacts by tailing the log rather than128 blocking the agent. While the run is in flight, advance the loop steps that129 don't depend on its output: cost-model the candidate models, pull benchmark130 context for the comparison, and scaffold the evidence artifacts131 ([`../understudy/reference.md`](../understudy/reference.md) § "Understudy132 Agent Improvement Report"). Surface a notification when the run completes;133 do not silently continue. For route work, route back to134 [`../optimize-workload/SKILL.md`](../optimize-workload/SKILL.md) once the run135 has produced candidate/proof evidence. See136 [`../../docs/background-ops.md`](../../docs/background-ops.md) for the exact137 tail/poll and exit-code patterns.138139## Keyless catalog sweep140141Use this recipe for the fastest frontier/open-weight comparison when the142developer does not have, or does not want to use, provider keys. The same143Understudy key can request supported Anthropic, OpenAI, and open-weight catalog144models by public id.1451461. Discover public model options:147148 ```sh149 understudy models list --json150 ```1511522. Ensure the eval workload exists on the gateway, then has **no route153 configured**. A route dialed to 0% still counts as configured and prevents154 request-time catalog resolution. Register from the local card if needed,155 then clear:156157 ```sh158 understudy workloads create --project-id <project-id> \159 --from-card .understudy/workload-discovery/workload-card.json160 understudy workloads route <workload-name> --project-id <project-id> --clear161 ```162163 Use the card's `workload_name` when set, otherwise its `workload_id`, as164 `<workload-name>`. Skip create if `understudy workloads list` already shows165 that name.1661673. Run the frozen eval once per catalog model by changing only the request-body168 `model` value. Send `x-understudy-project` and `x-understudy-workload` on169 every request so the run does not accidentally use the org default workload.1701714. Record the legibility headers for every row: `x-understudy-mode`,172 `x-understudy-route`, and `x-understudy-effective-model`. Treat rows where the173 requested model and effective model disagree as invalid for model comparison.1741755. Unknown ids for a keyless managed org return a clean catalog-miss 404 with176 available alternatives. Do not expect arbitrary frontier names to passthrough177 unless the org has its own managed provider key or the request supplies BYO178 credentials.1791806. Bound the run by row count, max tokens, and wall-clock time. New accounts get181 prepaid credit and async suspension, not a synchronous per-request spend182 reservation.183184## A/B model routing185186Use this recipe to A/B a chosen public model against passthrough while an eval187runs through the gateway. A typical consumer is188[`../optimize-agentic-workload/SKILL.md`](../optimize-agentic-workload/SKILL.md),189comparing a workload's quality and cost across the split.1901911. Discover public model options (public model IDs only; no supplier detail):192193 ```sh194 understudy models list --json195 ```196197 The same catalog is enumerable in-band via OpenAI-compatible198 `GET {gateway}/v1/models` with the `sk_*` key — `OpenAI(...).models.list()`199 works — useful from a harness that already speaks the OpenAI SDK.200201 The list is the remote ladder. It should include larger Gemma-family routes202 when enabled for the account, so the same API key can graduate a workload from203 the local Gemma 4 E2B first rung to larger Gemma variants or remote/hybrid204 routes without changing application code.205206 **Eval shortcut — request-time catalog resolution.** On a workload with **no207 route configured** (never routed, or explicitly `--clear`ed — a route dialed208 to 0% still counts as configured and stays passthrough), a request for any209 active catalog id (`"model": "glm-5.1"`, `"model": "gpt-5.5"`, or another210 listed id) is served from managed supply with no per-model setup. For a211 keyless managed org, unknown ids return a clean catalog-miss 404 with212 available alternatives instead of falling through to unpriced provider213 passthrough. So a model sweep needs exactly one unrouted workload and can214 iterate over listed catalog ids in the request body.215216 **Selecting the workload per request.** The gateway resolves which workload's217 route serves a request from two optional headers: `x-understudy-project`218 (project slug) and `x-understudy-workload` (workload name). Absent headers219 fall back to the org's default workload — whose route may rewrite the model.220 A bench harness should always send both headers and **never assume221 requested == served**: read the canonical legibility headers the gateway222 returns on every response — `x-understudy-mode` (managed/byo),223 `x-understudy-route` (`primary` / `understudy` / `fallback`), and224 `x-understudy-effective-model` (the public id that actually served) — and225 record the effective model, falling back to the response-body `model` field226 only if the headers are absent. Exclude runs where the requested and227 effective models disagree.2282292. Register the local workload card on the gateway if it is not already there.230 A local `workload-card.json` is only on disk until create runs — routing a231 card id that was never registered returns not-found.232233 ```sh234 understudy workloads create --project-id <project-id> \235 --from-card .understudy/workload-discovery/workload-card.json236 ```237238 Skip create when `understudy workloads list` already shows the card's239 `workload_name` (or `workload_id` when name is null).2402413. Route a workload to a model at a traffic percentage — a per-request split242 where that share goes to the routed model and the rest stays on passthrough.243 Pick a bounded share (e.g. 30%) to keep the comparison small. Use the same244 name create used (card `workload_name` or `workload_id`):245246 ```sh247 understudy workloads route <workload-name> --project-id <project-id> --model-id gemma-4-12b --traffic-pct 30248 ```249250 Clearing the route (`--clear` in place of the model/traffic flags) returns the251 workload to full passthrough. The hosted split semantics are documented at252 [docs.understudylabs.com/concepts/routing](https://docs.understudylabs.com/concepts/routing).2532544. Run the eval through the gateway so the routed model serves its share. Any255 local command works; an eval harness like a verifiers `vf-eval` run is typical.256257 ```sh258 understudy run -- vf-eval <eval-id>259 ```2602615. Prerequisite for a frontier comparison. For the split to compare the routed262 model against a frontier model, the non-routed (passthrough) share must have a263 configured managed provider key or BYO key; the managed catalog only resolves264 on no-route workloads. Without passthrough credentials, those non-routed265 requests error. For keyless accounts, use the **keyless catalog sweep** recipe266 first, then route only after choosing a candidate.267268## Diagnostics269270When the developer asks whether the gateway is causing errors, wants to check271provider health, or asks "is this us?", route to272[`../check-routing-health/SKILL.md`](../check-routing-health/SKILL.md). That273worker calls the read-only reporting endpoints (routing-status, provider-health,274compact status) and interprets the results. The full endpoint reference is at275[docs.understudylabs.com/reference/control-plane/reporting](https://docs.understudylabs.com/reference/control-plane/reporting).276277## Output Standard278279End with:280281- auth status without revealing secrets;282- project/key readiness;283- model route status when configured;284- command run or blocked;285- whether provider calls or hosted execution were approved;286- local artifact path or next CLI command to monitor.287288## References289290- [`references/frontier-keys.md`](references/frontier-keys.md) — managed catalog291 vs BYO `.env` keys vs skip, the allowlisted env vars, the292 secret-to-remote-infra recipe, and the installer mapping.293- Hosted contracts on the docs site —294 [routing semantics](https://docs.understudylabs.com/concepts/routing),295 [capture](https://docs.understudylabs.com/concepts/capture), the296 [control-plane API](https://docs.understudylabs.com/reference/control-plane)297 behind `workloads`/`routes`/`captures`,298 [reporting & health](https://docs.understudylabs.com/reference/control-plane/reporting)299 for self-service diagnostics, and the gateway300 [request headers](https://docs.understudylabs.com/reference/request-headers) /301 [response headers](https://docs.understudylabs.com/reference/response-headers).302303Domain depth in [`reference.md`](reference.md):304305- **Trace capture** — gateway or local trace capture without changing the app306 interface; capture calls/prompts/responses/tool calls/latency/errors/tokens/307 metadata; redact; skip upload in local-only / restricted (ZDR) modes; produce a308 trace inventory (defer call-site discovery to309 [`../capture-evidence/SKILL.md`](../capture-evidence/SKILL.md)).310- **Deploy and compare** — reproducible baseline, smallest coherent route/config311 change that solves the measured cause via the workloads API (or a local312 `understudy.yaml`), rollback, comparison evals, before/after metrics, surfaced313 regressions.314315For route selection and the fresh-pricing rule, see316[`../understudy/reference.md`](../understudy/reference.md); for measured claims,317[`../optimize-workload/SKILL.md`](../optimize-workload/SKILL.md).