# Preview Tools

Deploy any app to an ephemeral URL, screenshot it, and compare changes.

Plain text for agents: https://riddledc.com/docs/preview/markdown.md

## Which Tool Should I Use?

Pick the preview mode based on what must run in Riddle, not based on the framework name.

| Mode | Use When | Avoid When |
| --- | --- | --- |
| `riddle_preview` | You already have a static output directory such as `dist`, `build`, or `out`. | The app needs a live backend, SSR process, websocket server, or runtime filesystem writes. |
| `riddle_server_preview` | A server must run from uploaded files using a stock Docker image such as `node:22-slim`. | The app is just static files. Build locally and use `riddle_preview` instead. |
| `riddle_build_preview` | The project has a Dockerfile or needs custom system packages, native dependencies, or a reproducible image. | You only need to serve static output or run a simple command in a stock image. |

Rule of thumb: static output gets static preview; server process gets server preview; Dockerfile gets build preview.

## riddle_preview - Static Hosting

Static preview deploys built files to an ephemeral URL at `preview.riddledc.com`. Use it for SPAs, Vite builds, Create React App builds, static docs, Next.js static exports with `output: "export"`, and any other project where the final artifact is HTML, CSS, JavaScript, images, and other static files.

```bash
npm run build
# Upload dist, build, or out with riddle_preview.
```

Static preview is usually the fastest and cleanest option. It avoids remote dependency installation, remote frontend builds, and readiness polling. After the preview URL exists, screenshot it with Riddle's normal screenshot or script tools.

Use the `spa` framework hint for client-side routers that should fall back to `index.html`. Use `static` for multi-page static output where each path maps to a real file.

## riddle_server_preview - Server Apps

Server preview uploads a directory, starts a command inside a stock Docker image, waits for readiness, then runs Playwright against the running server.

Use it for apps that need a live server process: Express, Django, Rails, SSR Next, API-backed pages, websocket apps, or anything where page behavior depends on runtime server code.

```text
directory: /path/to/project
image: node:22-slim
command: npm ci && npm run build && npm run start -- -H 0.0.0.0 -p 3000
port: 3000
path: /dashboard
readiness_path: /dashboard
wait_for_selector: main
```

The uploaded directory must include everything your command needs, or the command must install/build it in the container. Remote install/build is valid for server apps, but it is slower and should not be used for static-only artifacts.

## riddle_build_preview - Custom Builds

Build preview builds a Docker image from the project's Dockerfile, runs that image, then captures browser proof.

The word "build" means Docker image build. It does not mean "run my frontend build command". Use build preview when a stock image is not enough, when system packages matter, or when the app already has a production Dockerfile.

```text
directory: /path/to/project
command: npm run start -- -H 0.0.0.0 -p 3000
port: 3000
path: /
keep_image_minutes: 30
```

Build preview is slower than static preview and usually slower than server preview on the first run. The payoff is fidelity: Riddle tests the same container shape your app expects.

## Timeouts

The preview tools have several timeout knobs. They measure different phases.

| Name | Meaning | When To Increase |
| --- | --- | --- |
| `timeout` | Total job execution budget. | Docker pulls, image builds, slow app startup, long proof scripts. |
| `readiness_timeout` | How long Riddle waits for the server to answer HTTP before browser capture. | Your command installs dependencies, builds assets, migrates data, or warms caches. |
| `navigation_timeout` | How long Playwright waits for `page.goto()`. | The page itself loads slowly after the server is ready. |
| `wait_for_selector` | A hydration or UI-ready selector to wait for before screenshot/script. | The document loads before React, Vue, or app state finishes rendering. |

If a preview fails while the server log still shows dependency installation, increase readiness time or avoid that phase by using the right mode. If the server is ready but the screenshot is too early, use `wait_for_selector` or increase `navigation_timeout`.

## Status Phases

Server preview and build preview expose a phase while the job runs. Treat it as the fastest way to know whether the job is uploading, building, starting the app, waiting for readiness, running browser proof, collecting artifacts, finalizing outputs, complete, or failed.

Poll responses include `phase`, `phase_updated_at`, `phase_details`, and a derived `progress` object. Use `progress.label` and `progress.percent` for user-facing updates. Use `progress.terminal` to distinguish running states from final `complete` or `failed` responses.

```json
{
  "status": "running",
  "phase": "waiting_for_readiness",
  "phase_updated_at": "2026-05-02T22:10:00.000Z",
  "phase_details": {
    "port": 3000,
    "readiness_path": "/health",
    "readiness_timeout": 30
  },
  "progress": {
    "step": 9,
    "total": 13,
    "percent": 69,
    "label": "Waiting for readiness",
    "terminal": false
  },
  "outputs": []
}
```

Common phases:

- `awaiting_upload`: the job exists and is waiting for the uploaded tarball.
- `queued`: the upload is complete and a worker has not leased it yet.
- `leased`: a worker accepted the job.
- `pulling_image`: server preview is pulling the stock Docker image.
- `downloading_upload` and `extracting_upload`: the worker is preparing the uploaded files.
- `running_audit`: build preview is running the optional audit.
- `building_image`: build preview is building the Dockerfile image.
- `creating_container` and `starting_server`: the server process is starting.
- `waiting_for_readiness`: Riddle is polling the configured readiness URL.
- `running_browser_proof`: Playwright is navigating, screenshotting, or running the script.
- `collecting_artifacts`: logs, screenshots, HAR, console output, and JSON artifacts are being uploaded.
- `finalizing_result`: worker execution finished and Riddle is persisting final outputs.
- `complete` or `failed`: the run is finished.

If a wrapper times out, report the last phase in the error. If the phase is still `waiting_for_readiness` and logs show `npm install`, `npm ci`, `next build`, `vite build`, or similar work, increase `readiness_timeout`, prebuild/upload static output, or switch to build preview when a Dockerfile is the real contract.

Readiness failures include the server log tail in `phase_details.error`. Failed `riddle_server_preview` responses can also include `server_log`, which is the app command output captured from inside the preview container. Check `server_log` first when the phase is `starting_server` or `waiting_for_readiness`.

## Proof Guidance

For Riddle Proof, preview choice is part of the proof contract.

- For a static export, build locally and proof the exported directory with static preview.
- For a server app, use server preview and keep the startup command realistic.
- For a Dockerized app, use build preview and let the Dockerfile define the environment.
- Save a JSON artifact that states the route, assertions, and checks performed.
- Fail the proof if the browser script reports an error, even if the preview job itself completed.

## Gotchas

- Do not use server preview for static exports. It works only if you add a static server command, and it is usually slower than static preview.
- Do not confuse build preview with frontend build output. Build preview is for Dockerfile builds.
- Be careful with exclusions. Excluding `node_modules` is good when the command runs `npm ci`, but bad when your uploaded server expects installed dependencies.
- Use readiness paths intentionally. A lightweight `/health` path can prove the server is up, but a page path can catch route-specific failures earlier.
- Watch script errors. A preview can complete while the browser script saved an error artifact. Treat `script_error` as a failed proof.

