---
name: ronda
description: Use to edit code in a web application the user has connected to ronda (https://useronda.com). Opens a sandboxed copy of their repo, applies natural-language edits, returns a live preview URL, and can open a GitHub pull request on demand. Pick this skill whenever the user wants to change UI, copy, styles, components, or any code in a project they've connected to ronda — including phrases like "edit the landing page", "change the hero copy", "tweak the pricing card", "make this look better", or "open a PR for that".
---

# ronda — sandboxed code edits over MCP

ronda exposes its core verbs over the Model Context Protocol at
`https://mcp.useronda.com`. You drive a per-user sandbox: it clones the user's
repo, boots the dev server, takes natural-language edit instructions, and
gives you back a live preview URL plus a diff. When the user is happy, you
open a pull request against their default branch.

## One-time setup (the user does this once)

ronda is added as a custom MCP connector in the user's Claude client:

1. In Claude → **Settings → Connectors → Add custom connector**.
2. Enter `https://mcp.useronda.com` and click Add.
3. The user is redirected to ronda, logs in (or signs up at `https://useronda.com`),
   reviews requested scopes, and clicks **Allow**.
4. From then on, you have the tools below available in this chat.

You do not handle OAuth. The Claude client handles registration, consent, and
token rotation transparently. If a tool call returns `MCP_UNAUTHORIZED` (HTTP
401), the token is malformed, expired, or revoked — the client refreshes it via
`grant_type=refresh_token` and retries; if the refresh fails, tell the user to
reconnect at `Settings → Connectors`.

## Tools

All tools are workspace-scoped via the user's connection. Calls return JSON.

### Discovery
- `list_projects()` — every project in the user's workspace (newest first, 100 cap).
  Each entry: `{ id, name, githubRepo, devCommand, devPort, primaryServiceName }`.

### Session lifecycle
- `open_session({ projectId, initialInstruction? })` — boot or reuse a sandbox.
  Returns immediately with `{ sessionId, isNew, sandboxStatus, previewUrl, branch,
  pollAfterMs, estimatedSecondsRemaining }`. `previewUrl` is `null` until the
  sandbox reaches `READY`. Reuse logic: live sandbox → reuse; archived sandbox →
  resume in background; otherwise create new.
- `get_session({ sessionId })` — current state + the last 50 edits.
- `list_sessions({ projectId, includeDestroyed? })` — sandboxes for a project +
  the concurrent-sandbox cap.
- `destroy_session({ sessionId })` — explicit teardown. Idempotent.

### Editing
- `instruct({ sessionId, message, selectedFiles?, idempotencyKey? })` — queue an
  edit. Returns `{ editId, status, pollAfterMs, estimatedSecondsRemaining }`.
  Same `idempotencyKey` within 24h returns the existing `editId` — safe to retry.
- `get_edit({ editId })` — poll. Statuses: `queued | in_progress | applied |
  failed | cancelled`. On `applied`: `{ summary, filesChanged, previewUrl,
  diffUrl }`. On `failed`: `{ failureReason, failureHint }`.

### Shipping
- `open_pr({ sessionId, title?, body?, idempotencyKey? })` — push the session
  branch and open a PR against the project's default branch. Refuses with
  `NO_CHANGES_TO_PR` if no edits have been applied.

### Variants (taste-driven A/B without analytics)
- `request_variants({ sessionId, message, count })` — generate 2 or 3 takes
  on the same change so the user picks a winner by taste. Each variant is
  committed to its own branch; the sandbox restarts with a variant switcher
  injected so the user (or you, via the `previewUrl`) can browse them.
  **Synchronous, 30-90s** — runs the agent inline and returns when all
  variants are committed. Returns `{ sessionVariantId, descriptor:
  { variants:[{key,label}], controlKey }, summary, sandboxRestarting:true,
  pollAfterMs, estimatedSecondsRemaining }`. Use for marketing copy, hero
  layouts, brand-tone choices.
- `resolve_variant({ sessionId, pickedKey, sessionVariantId? })` — pick the
  winner. Cleanup agent removes the variant switch and keeps `pickedKey` as
  the sole code path. **Synchronous, 30-60s.** Sandbox restarts at the end
  so the variant pill goes away. Omit `sessionVariantId` to resolve the
  currently-active set.

### Experiments (PostHog A/B with real metrics)
- `apply_experiment({ sessionId, proposalMessageId? })` — turn an
  `experiment_proposal` chat message into a live A/B test. Writes the
  variant code, creates the feature flag, creates the experiment in
  PostHog — **stops at launch** so the user clicks Launch in PostHog's UI
  when ready for traffic. Workflow: call `instruct` with a split-test
  phrasing first (e.g. "split-test the hero CTA against an alternative"),
  poll until applied, then look for an assistant message with
  `metadata.kind === 'experiment_proposal'`, then call `apply_experiment`.
  Omit `proposalMessageId` to apply the most recent un-applied proposal.
  Requires the workspace to have connected PostHog under
  `/app/settings/connectors`; returns `POSTHOG_CONNECTION_NOT_FOUND` if
  not.

### Resources (read-only)
- `project://{id}/intelligence` — profile, design system, conventions
- `project://{id}/code-map` — cached codebase digest
- `session://{id}/diff` — full diff vs base
- `session://{id}/messages` — chat history (last 500)

## Standard workflow

User: "change the hero CTA to 'Start free trial'"

1. **Pick the project.** If `list_projects()` returns one, use it. If many, ask
   the user which one. Cache the choice for the rest of the conversation.
2. **Open a session.** `open_session({ projectId })`. Show the user a brief
   "spinning up your sandbox" message; do not block waiting.
3. **Queue the edit.** `instruct({ sessionId, message: "<user's exact ask>" })`.
   Echo the change you understood ("I'll change the hero CTA to 'Start free
   trial'") and the preview URL once it's known.
4. **Poll.** `get_edit({ editId })` every `pollAfterMs` (default 3s). While
   polling, do not chat — wait for terminal status.
5. **Report.** On `applied`: share the summary, the previewUrl as a clickable
   link, and the list of files changed. On `failed`: share `failureHint`
   verbatim and ask whether to retry, rephrase, or stop.
6. **Open PR only when asked.** Do NOT auto-PR after every edit. Wait for "ship
   it" / "open a PR" / equivalent. Then call `open_pr` with a short, factual
   title derived from the edit summary.

## Behavior rules

- **The preview URL IS the proof.** Always surface it inline — it's how the user
  verifies the edit worked. A successful tool response without a previewUrl
  shown to the user is a missed delivery.
- **One edit per session at a time.** A second `instruct` on the same session
  while one is in flight will queue. Don't fan out across sessions for related
  edits; let them queue.
- **Don't retry on failure without asking.** `failureReason` covers
  `credit_balance`, `provider_outage`, `commit_failed`, `env_missing`, `generic`.
  Each warrants a different user response — don't paper over them.
- **Use idempotency keys** on `instruct` and `open_pr` if you might retry. Same
  key within 24h returns the existing record.
- **Don't speak about ronda's internals.** From the user's perspective they're
  editing their app, not learning about sandboxes. Keep status messages short
  ("working on it…", "applied, here's a preview", "opened PR #42").
- **No sandbox housekeeping.** Sandboxes archive themselves after idle timeout
  and resume on next use. Do not call `destroy_session` unless the user
  explicitly asks to throw work away.

## Scopes the connector requests

`session:read`, `session:create`, `session:instruct`, `pr:open`, `project:read`
are granted by default. `session:destroy` and `project:create` require an
elevated consent. If you call a tool and get `MCP_FORBIDDEN_SCOPE`, tell the
user to reconnect with the elevated scope set.

## Errors you'll see and what they mean

- `PROJECT_NOT_FOUND` / `SESSION_NOT_FOUND` / `EDIT_NOT_FOUND` — bad id,
  surface to user.
- `SANDBOX_LIMIT_REACHED` — workspace plan's concurrent-sandbox cap; tell the
  user to destroy a stale one or upgrade.
- `FREE_PLAN_BYOK_REQUIRED` — the workspace hasn't configured an Anthropic key
  in ronda yet. This error carries a `link` (see "Guided setup links" below):
  surface that button so the user adds their key, then retry the edit once they
  confirm.
- `GITHUB_NOT_CONNECTED` — the project has no GitHub connection yet. Also
  carries a `link`: surface it so the user connects a repository, then retry
  `open_session`.
- `NO_CHANGES_TO_PR` — `open_pr` called before any edit was applied.
- `MCP_RATE_LIMITED` — 60 calls/min ceiling hit; back off per `retryAfterMs`.
- `MCP_UNAUTHORIZED` (HTTP 401) — token is malformed, expired, or revoked;
  refresh it via `grant_type=refresh_token` and retry, or ask the user to
  reconnect at `Settings → Connectors` if the refresh fails.
- `VARIANT_ALREADY_ACTIVE` — a variant set is already live on this sandbox;
  call `resolve_variant` first (or open a new session) before requesting more.
- `SESSION_VARIANT_NOT_FOUND` — `resolve_variant` called with no active set;
  generate one via `request_variants` first.
- `EXPERIMENT_ALREADY_ACTIVE` — one experiment per (project, sandbox);
  use `list_sessions` to find a different sandbox or wait.
- `POSTHOG_CONNECTION_NOT_FOUND` — workspace hasn't connected PostHog;
  point the user at `https://app.useronda.com/app/settings/connectors`.
- `TOOL_NOT_CONFIGURED` — `apply_experiment` needs
  `NEXT_PUBLIC_POSTHOG_KEY` + `NEXT_PUBLIC_POSTHOG_HOST` filled in the
  project's Environment tab.

## Guided setup links

Some errors are setup dead-ends the human can fix in one step rather than
something the agent did wrong. Those errors — currently
`FREE_PLAN_BYOK_REQUIRED` and `GITHUB_NOT_CONNECTED` — carry an extra `link`
field in the error body:

```json
{
  "code": "FREE_PLAN_BYOK_REQUIRED",
  "message": "Workspace requires an Anthropic key (BYOK) to run edits.",
  "hint": "Open the link below to add your Anthropic key, then retry the edit.",
  "next": "Surface `link` as a button; abort the current edit dispatch. Retry once the user confirms they connected the key.",
  "link": { "url": "https://app.useronda.com/app/settings/models?return=mcp", "label": "Connect Anthropic key" }
}
```

When you see a `link`, render it as a clickable button using its `label`. The
URL always points at `https://app.useronda.com`, the origin ronda owns — the
user opens it in their own browser to fix the missing setup step. The link
carries a `?return=mcp` hint, so it lands on a focused one-step completion page
(just the GitHub-connect or Anthropic-key step the error demanded, not the full
settings page) that tells the user to switch back to their chat once they're
done. Do not embed the credential form yourself and do not paste keys into chat
— the user enters them on `app.useronda.com` in their own browser session.
After they confirm they're done, retry the original tool call.

## Cost note

ronda calls Anthropic on the user's behalf using THEIR Anthropic API key
(BYOK). Edit instructions can cost real money. Don't loop or speculate — one
clear edit per user request.
