# Working with agents

> Connect your coding agent to Fradiation over MCP, what it can do there, the machine-readable files and skill, deploying with an API token, the upload API, and prompts to give it.

Most games here are built with a coding agent in the loop. Agents read the docs faster than you do and argue with them less.

Claude Code, Cursor, Codex and similar tools can do the work between "it runs on my machine" and "testers are playing it". The quickest way to give them everything is the MCP server: connect once, then ask for what you want.

## Connect your agent

The Fradiation MCP server gives your agent the docs, the build rules and your games in one place, at `https://www.fradiation.games/api/mcp`. Add it to your agent. The first time you connect, a browser opens: sign in with Discord or GitHub, see what the agent will be able to do, and click **Allow**. There's no token to copy.

**The Claude app (desktop and claude.ai).** Add Fradiation as a custom connector. No terminal is needed. It works in the desktop app's Code and Chat tabs and on claude.ai, and the Claude Code CLI loads it too when you're signed in with the same account.

1. Open your connectors: in the desktop app, **Settings → Connectors** (or **+** by the prompt → **Connectors** → **Manage connectors**). On the web, go to [claude.ai/customize/connectors](https://claude.ai/customize/connectors).
2. Click **+ Add**, then **Add custom connector**.
3. Name it `Fradiation` and paste `https://www.fradiation.games/api/mcp`. Click **Continue**.
4. For the OAuth client, choose **Register automatically**. Click **Add**, then **Connect**, and sign in.
5. Start a new session. Fradiation's tools are there.

**Claude Code in a terminal.** If you only use the CLI, you can add the server there instead. Run this once, then type `/mcp` in Claude Code, pick `fradiation` and sign in. `--scope user` makes it work in every project.

```bash
claude mcp add --transport http --scope user fradiation https://www.fradiation.games/api/mcp
```

The desktop app also loads servers added this way, but only when a session starts, and it has no `/mcp` panel to sign in or reconnect them. In the desktop app, use the custom connector.

**Cursor.** In `~/.cursor/mcp.json`, or `.cursor/mcp.json` in the project. Cursor shows a **Connect** button that opens the sign-in.

```json
{
  "mcpServers": {
    "fradiation": { "url": "https://www.fradiation.games/api/mcp" }
  }
}
```

**VS Code.** In `.vscode/mcp.json`. VS Code asks you to sign in the first time the server starts. The file holds no secret, so it's safe to commit.

```json
{
  "servers": {
    "fradiation": { "type": "http", "url": "https://www.fradiation.games/api/mcp" }
  }
}
```

**Codex.** In `~/.codex/config.toml`, then run `codex mcp login fradiation`.

```toml
[mcp_servers.fradiation]
url = "https://www.fradiation.games/api/mcp"
```

**Anything else** that supports MCP sign-in: the URL is all it needs. For a client that only runs local servers, bridge it with `npx -y mcp-remote https://www.fradiation.games/api/mcp`, which handles the sign-in too.

Then try:

```text
Use the fradiation MCP server to put this game on Fradiation: wire in the SDK with a few mutations and a high-score board, make a cover, then deploy it to a draft.
```

### Signing in

- The game tools need a badge and developer access. A signed-in account without developer access can still read the docs and check files; the agent says how to get access.
- The agent stays signed in. Its access lasts an hour at a time and renews in the background. An agent that isn't used for 90 days signs in again.
- When Fradiation adds or changes tools, start a new session to see them. Agents load a server's tool list when a session starts.
- **Signed-in agents**, at the bottom of **Agents & tokens** (`/dev/agents`, in the menu under your avatar), lists every agent you've allowed. **Hold to remove** signs one out at once: its next request fails and it has to sign in again.

### With a token instead

For a client that can't open a browser (CI, a remote box, an older client), send an API token as a header instead. Under **Agents & tokens** (`/dev/agents`), click **No browser sign-in? Use a token**, then **Create a token for it**: the snippet for your agent fills in with a new 90-day token. Any token from the **API tokens** panel works too. For Claude Code:

```bash
claude mcp add --transport http --scope user fradiation https://www.fradiation.games/api/mcp --header "Authorization: Bearer <token>"
```

Other clients take the same header in their config (`headers` in JSON, `http_headers` in Codex's TOML). VS Code can prompt for the token and keep it in its secret storage; the panel shows that snippet.

### What it can do

| Tool | What it does | Needs |
| --- | --- | --- |
| `read_doc` | Any page of these docs, or the skill, as Markdown | Any account |
| `get_build_rules` | The upload rules and the `fradiation.json` schema, from the code that enforces them | Any account |
| `check_manifest` | Checks a `fradiation.json` the way an upload does, and lists every problem | Any account |
| `list_games` | Your games: status, live build, plays, new feedback | Yes |
| `get_game` | One game: details, what it still needs before containment, mutations, boards, recent builds, playtest | Yes |
| `create_game` | Creates a draft. The slug is permanent. | Yes |
| `update_game` | Title, description, engine and AI disclosure | Yes |
| `begin_upload` | Checks a build's file list and `fradiation.json`, and gives a presigned upload URL for each file the game doesn't have (below) | Yes |
| `upload_urls` | Fresh upload URLs for whatever an open upload is still missing | Yes |
| `finish_upload` | Seals the upload, and optionally sends it to testers or makes it live | Yes |
| `cancel_upload` | Gives up on an open upload | Yes |
| `upload_cover` | A presigned URL to upload the cover to (below) | Yes |
| `make_build_live` | Ships a build, or rolls back to an older one | Yes |
| `start_playtest` | Opens the private test link, optionally with a given build | Yes |
| `get_feedback` | Playtest feedback with play time, device, and the errors and events the SDK caught, plus sessions that hit errors | Yes |
| `set_feedback_status` | Marks feedback read or addressed | Yes |
| `submit_for_judgment` | Sends a draft into containment | Yes |

The server tells the agent to ask you before it makes a build live on a game that isn't a draft, and before it submits one for judgment. The agent decides whether it listens, so read what it plans before you approve those calls.

Still in the browser: the playtest's note and questions, deleting a draft, pinning builds, and managing tokens and signed-in agents.

### How uploads work

The build is on your machine and the server isn't, so the agent uploads it in three steps. No token or password reaches the agent or its shell, and it runs no downloaded code.

1. **`begin_upload`.** The agent hashes every file in the build folder (path, size, SHA-256: `sha256sum` in bash, `Get-FileHash` in PowerShell) and sends the list, with the contents of `fradiation.json`. The server checks them against the same rules as any upload: paths, file types, limits, `.br`/`.gz`, the entry file and the manifest. Then it answers with a **presigned URL** for each file the game doesn't already have.
2. **PUTs.** The agent sends each file's bytes to its URL with plain `curl` or `Invoke-RestMethod`. No headers are needed. Files over 64 MB go up in parts, one URL per part.
3. **`finish_upload`.** The server checks that every file arrived intact and seals the build. With `playtest: true` the build goes to testers, and with `live: true` it goes to players. It answers with the build id, its URL and the testers link. If something is missing, it says what and leaves the upload open; `upload_urls` gives fresh URLs.

A presigned URL allows one thing: uploading one file, whose bytes must match the hash it was signed for, to one game, within the hour. It isn't an account credential and can't do anything else. Files the game already has from earlier builds aren't sent again, so an update only uploads what changed. An upload counts toward the 20 a day, as any upload does.

### How covers work

`upload_cover` answers with one presigned URL, good for 15 minutes. The agent PUTs the image to it the same way. The site does what the uploader in your browser does: it crops the image to 4:3 from the centre, scales it to 800×600 and stores a JPEG with the metadata stripped.

- PNG, JPEG, WebP, AVIF or GIF, up to 15 MB.
- Make it 4:3, or wider with the subject in the middle. Keep text away from the edges; the crop and small card sizes cut it.
- It replaces the current cover at once, everywhere the game appears.

### Long builds

Agents stay signed in. An access token lasts an hour and renews in the background, and an agent that isn't used for 90 days signs in again. A Unity build that takes 20 minutes doesn't interrupt anything.

If your agent drives an open Unity editor through an editor-automation tool, have it call `FradiationBuild.BuildDeferred()` from the [Unity kit](/docs/unity) rather than `Build()`. A long build run inside a tool request can be cut off by the request's timeout ("Tundra build interrupted"). `BuildDeferred()` returns at once and builds on the next editor tick. It writes `Builds/Fradiation.status.json` (`queued`, `building`, `succeeded` or `failed`) for the agent to poll.

## What an agent can do without MCP

- Prepare a build for any engine: Unity, Godot, Three.js/Vite or plain HTML5.
- Write and validate `fradiation.json`.
- Wire in the SDK: mutations, scoreboards, playtest events.
- Check the build against the upload rules, and package it as a zip.
- Deploy it with an API token in `FRADIATION_TOKEN`: upload it, send it to testers or make it live, and roll back. See [Deploy from the command line](#deploy-from-the-command-line).

Without MCP, creating the game, its details, playtests and submitting to containment happen in the browser.

> [!NOTE]
> Without MCP, create the game first with **New game** on My games (`/dev`). The slug you pick there is the game's origin (`https://<slug>.containment.cloud`), and every deploy names it.

## Machine-readable files

Every doc page has a raw Markdown twin: add `.md` to the address. `/docs/sdk` is also at `/docs/sdk.md`. The rest are below.

| File | What it is | Use it when |
| --- | --- | --- |
| `https://www.fradiation.games/llms.txt` | Index of every doc, as Markdown links | An agent has no other context. Start here. |
| `https://www.fradiation.games/llms-full.txt` | Every doc concatenated | You paste docs into a chat that can't browse. |
| `https://www.fradiation.games/docs/<slug>.md` | Raw Markdown of one page | The agent needs one topic, for example `/docs/unity.md`. |
| `https://www.fradiation.games/fradiation.schema.json` | JSON Schema for `fradiation.json` | Editor autocomplete, and the source for field names and mutation icon names. |
| `https://www.fradiation.games/docs/build-rules.json` | Limits, allowed file types and path rules, generated from the code the uploader and server use | Anything about limits. If a doc and this file disagree, this file wins. |
| `https://www.fradiation.games/sdk/fradiation-sdk.js` | The SDK as a script tag. Sets `window.fradiation`. | Unity templates, plain HTML, anything without a bundler. |
| `https://www.fradiation.games/sdk/fradiation-sdk.mjs` | The SDK as an ES module | Vite and other bundlers. |
| `https://www.fradiation.games/sdk/fradiation-sdk.d.mts` | TypeScript types for the ES module | TypeScript projects. Save it next to `fradiation-sdk.mjs`. |
| `https://www.fradiation.games/kits/fradiation-unity-kit.zip` | Unity kit: build menu, batch method, C# API, WebGL template | Any Unity project. See [Unity](/docs/unity). |
| `https://www.fradiation.games/skills/fradiation/SKILL.md` | Agent skill for preparing, checking and deploying builds | You want the agent to know the rules without being told. |
| `https://www.fradiation.games/skills/fradiation/check-build.mjs` | Build checker, packager and deploy tool | Before every upload, and for deploying. |
| `https://www.fradiation.games/skills/fradiation.zip` | The skill folder as a zip | You prefer one download to two. |

> [!NOTE]
> The SDK is not on npm. Download one of the SDK files and ship it inside your build.

## The Fradiation skill

A skill is a folder an agent loads when a task matches it. The Fradiation skill holds the build rules, the workflow for each engine, the `fradiation.json` format and the SDK calls. It comes with `check-build.mjs`, so the agent can verify its own work and, with a token, deploy it. It loads when a task involves a Fradiation build, `fradiation.json`, mutations, scoreboards, the SDK, deploying, or an upload that fails.

### Install for Claude Code

Two places work:

- Project: `.claude/skills/fradiation/`. Commit it and everyone on the repo gets it.
- Personal: `~/.claude/skills/fradiation/`. Every project on your machine gets it.

Project install:

```bash
DIR=.claude/skills/fradiation   # personal install: DIR="$HOME/.claude/skills/fradiation"
mkdir -p "$DIR"
curl -fsSL https://www.fradiation.games/skills/fradiation/SKILL.md -o "$DIR/SKILL.md"
curl -fsSL https://www.fradiation.games/skills/fradiation/check-build.mjs -o "$DIR/check-build.mjs"
```

```powershell
$dir = ".claude/skills/fradiation"   # personal install: $dir = "$HOME\.claude\skills\fradiation"
New-Item -ItemType Directory -Force $dir | Out-Null
Invoke-WebRequest -UseBasicParsing https://www.fradiation.games/skills/fradiation/SKILL.md -OutFile "$dir/SKILL.md"
Invoke-WebRequest -UseBasicParsing https://www.fradiation.games/skills/fradiation/check-build.mjs -OutFile "$dir/check-build.mjs"
```

Or download the zip. It holds the `fradiation` folder, so unzip it into `skills/`:

```bash
curl -fsSL https://www.fradiation.games/skills/fradiation.zip -o fradiation.zip
unzip -o fradiation.zip -d .claude/skills
rm fradiation.zip
```

```powershell
Invoke-WebRequest -UseBasicParsing https://www.fradiation.games/skills/fradiation.zip -OutFile fradiation.zip
Expand-Archive fradiation.zip -DestinationPath .claude/skills -Force
Remove-Item fradiation.zip
```

Either way, `.claude/skills/fradiation/SKILL.md` must exist afterwards. Restart Claude Code if it was running. Describe a Fradiation task and the skill loads on its own, or call it by name with `/fradiation`.

### Other agents

Agents that read `SKILL.md` files can use the same folder, in the place their tool looks for skills. Agents that read plain Markdown can be pointed at the `SKILL.md` URL, or at `https://www.fradiation.games/llms.txt`. To make that permanent, add a line to your agent instructions file (`AGENTS.md`, `CLAUDE.md`, or your editor's rules):

```text
Fradiation Games build rules, SDK and fradiation.json: https://www.fradiation.games/llms.txt
Check a build with: node check-build.mjs <folder>  (https://www.fradiation.games/skills/fradiation/check-build.mjs)
Deploy with: node check-build.mjs deploy <folder> --playtest  (FRADIATION_TOKEN is set; never print it)
```

## check-build.mjs

A zero-dependency script for Node 20 or later. It applies the upload rules on your machine, so you find out about a bad build before anything is uploaded. With an API token it also deploys; see [Deploy from the command line](#deploy-from-the-command-line).

```bash
node check-build.mjs Builds/Fradiation                  # check a folder
node check-build.mjs build.zip                          # check a zip
node check-build.mjs Builds/Fradiation --json           # machine-readable report, for agents
node check-build.mjs pack Builds/Fradiation             # check, then zip it the way the uploader expects
node check-build.mjs pack Builds/Fradiation game.zip    # same, to a path you choose
```

If you installed the skill, the script is at `.claude/skills/fradiation/check-build.mjs`.

It checks the rules in `build-rules.json`:

- The entry file (`index.html`, or `entry` in `fradiation.json`) sits at the top.
- At most 1,000 files, 500 MB in total, 200 MB per file.
- Every file type is on the allowed list, and every path follows the path rules.
- `fradiation.json` follows the schema: viewport, threads, mutations, boards.

The exit code is non-zero when there are problems, so it works in CI and in an agent loop. `pack` checks the folder, then zips it. See [Builds](/docs/builds) for the rules in full.

## Deploy from the command line

With an API token, the same script uploads builds. No browser, no zip.

```bash
node check-build.mjs deploy dist --game my-game --playtest --notes "Fixed the boss fight"
```

### Make a token

1. Open **Agents & tokens** (`/dev/agents`, in the menu under your avatar). The **API tokens** panel is under Connect your agent.
2. Give it a **Name** that says where it will live: `laptop`, `CI`, `claude`.
3. Pick **Games**: all your games, or one game. A one-game token can't touch your other games, and commands with it don't need `--game`.
4. Pick when it **Expires**: 7, 30 or 90 days, or a year.
5. Click **Create token** and copy it. It is shown once. The site keeps only a hash of it.

Put it in the environment as `FRADIATION_TOKEN`:

```bash
export FRADIATION_TOKEN=frad_...
```

```powershell
$env:FRADIATION_TOKEN = "frad_..."
```

In CI, store it as a secret.

- A token acts as you, on your own games only. That holds for admins too.
- You can have 10 tokens at a time. **Hold to revoke** stops one at once. Expired tokens stay in the list, marked expired, until you remove them.
- A token stops working if your developer access is removed.
- The list shows when each token was last used.

> [!CAUTION]
> Anyone holding the token can upload builds to your games and make them live. Don't commit it, paste it into a chat or put it in a build. Give an agent a one-game token with a short expiry. If a token leaks, revoke it.

### deploy

```bash
node check-build.mjs deploy <folder-or-zip> [--game <slug>] [--live] [--playtest] [--notes <text> | --notes-file <file>] [--viewport <w>x<h>] [--json]
```

What it does:

1. Checks the build, exactly like `node check-build.mjs <folder>`. On errors it stops without contacting the site.
2. Computes every file's SHA-256 and sends the file list to the site.
3. Uploads only the files the game doesn't have yet, straight to the play server, four at a time. Files over 64 MB go up in parts.
4. The site checks that every file arrived at its full size, then seals the build.
5. Prints the build id, its URL, the game page and its Manage page.

| Option | Effect |
| --- | --- |
| `--game <slug>` | The game. Leave it out when the token only works for one game. |
| `--live` | Make the build live once it's sealed. See the table below. |
| `--playtest` | Send the build to the game's playtest. Testers get it on their next load, with the patch notes. Start the playtest on the game's Manage page first. |
| `--notes <text>` | Patch notes, up to 2,000 characters. Shown in the game's update log. |
| `--notes-file <file>` | Patch notes from a file. Easier for several lines. |
| `--viewport <w>x<h>` | The stage size, used only when `fradiation.json` has no `viewport`. Without either, a deploy keeps the live build's size, or uses 1280x720. |
| `--json` | Print one JSON object and nothing else. |

When the build goes live:

| Game status | Without `--live` | With `--live` |
| --- | --- | --- |
| Draft | Live. A draft always runs its newest build. Only you and admins can see a draft. | Live |
| Any game with no live build | Live | Live |
| In containment, released or buried | Sealed, not live. Players keep the build they have. | Live. Players get it on their next load. |

A deploy is an upload like any other: it counts toward the 20 uploads a day and the storage limit, and it shows in the Builds list on the game's Manage page with its patch notes. If it stops half-way, on an error or Ctrl+C, it cancels the upload and the site cleans up what was sent.

### Other commands

```bash
node check-build.mjs builds --game <slug>               # the 30 newest builds: id, date, files, size, tags, notes
node check-build.mjs live <buildId> --game <slug>       # make a build live: ship an update, or roll back
node check-build.mjs playtest <buildId> --game <slug>   # send a build to testers
node check-build.mjs whoami                             # the token's developer, expiry and games
node check-build.mjs cover cover.png --game <slug>      # set the cover; the site crops it to 800x600
```

A build id can be shortened to its first 7 characters, as the Manage page shows it, or any unique start of at least 4. `live` and `playtest` refuse builds whose files were deleted.

### JSON output and exit codes

Every command exits with 0 on success and 1 on failure, and takes `--json`. A deploy prints:

```json
{
  "ok": true,
  "game": "my-game",
  "status": "released",
  "buildId": "3f2a9c1d8e7b6a5f",
  "live": false,
  "playtest": true,
  "url": "https://my-game.containment.cloud/3f2a9c1d8e7b6a5f/index.html",
  "page": "https://www.fradiation.games/games/my-game",
  "dashboard": "https://www.fradiation.games/dev/my-game",
  "testers": "https://www.fradiation.games/t/k3x9m2p7q4w8",
  "files": 12,
  "totalBytes": 3565158,
  "newFiles": 1,
  "uploadedBytes": 1634,
  "warnings": []
}
```

A failure prints `ok: false`, the `stage` it failed at (`check`, `hash`, `begin`, `upload` or `finish`) and `errors` with a `code` and a `message`. The codes are the API's (below), plus the checker's own for a build that fails the check, `no-token` when `FRADIATION_TOKEN` isn't set, `network` when the site can't be reached, and `upload` when the play server refused a file.

### In CI

A GitHub Actions step that sends every push on `main` to testers. Use a one-game token.

```yaml
- name: Deploy to Fradiation
  env:
    FRADIATION_TOKEN: ${{ secrets.FRADIATION_TOKEN }}
  run: |
    curl -fsSL https://www.fradiation.games/skills/fradiation/check-build.mjs -o check-build.mjs
    git log -1 --pretty=%B > notes.txt
    node check-build.mjs deploy dist --playtest --notes-file notes.txt
```

## The upload API

`check-build.mjs` is a client of this API, and its source is a readable reference implementation. Use the API directly only if you're writing another client.

- Base URL: `https://www.fradiation.games/api/v1`.
- Every request sends `Authorization: Bearer <token>`.
- Bodies are JSON. Every answer is `{ "ok": true, ... }` or `{ "ok": false, "code": "...", "error": "..." }`.
- Every endpoint takes `game`, the slug. Leave it out with a one-game token.

| Endpoint | Body | Answer |
| --- | --- | --- |
| `GET /me` | none | `developer`, `token` (`name`, `game`, `expiresAt`), `games` (`slug`, `title`, `status`, `page`, `dashboard`) |
| `GET /builds?game=<slug>` | none | `game` (`slug`, `title`, `status`, `page`, `dashboard`, `testers` if a playtest is open), `builds`: the 30 newest, each with `id`, `status`, `live`, `testing`, `pinned`, `files`, `bytes`, `uploadedBytes`, `notes`, `createdAt`, `filesDeleteAt`, `filesDeletedAt`, `url` |
| `POST /builds/begin` | `files` (`path`, `size`, `sha256`), `manifest`, `viewport`, `notes` | `buildId`, `prefix`, `missing`, `uploadedBytes`, `grant`, `endpoint`, `partBytes` |
| `POST /builds/finish` | `buildId`, `live`, `playtest` | `buildId`, `status`, `live`, `playtest`, `url`, `page`, `dashboard`, `testers` |
| `POST /builds/cancel` | `buildId` | `buildId` |
| `POST /builds/live` | `buildId` | `buildId`, `status`, `url`, `page`, `dashboard` |
| `POST /builds/playtest` | `buildId` | `buildId`, `page`, `dashboard`, `testers` |
| `POST /cover?game=<slug>` | the image's bytes (PNG, JPEG, WebP, AVIF or GIF, up to 15 MB) | `game`, `cover` (its URL), `page`, `dashboard` |

| Status | `code` | When |
| --- | --- | --- |
| 400 | `invalid` | The body breaks a rule. The message names the file or field. |
| 401 | `auth` | No token, or it's unknown, revoked or expired. |
| 403 | `forbidden` | Not your game, a one-game token used for another game, or no developer access. |
| 404 | `missing` | No such game or build, or the upload isn't open any more. |
| 429 | `rate` | 20 uploads in a day, or the storage limit. |
| 500 | `server` | Something broke on our side. Try again. |

### An upload, step by step

1. **begin.** Send every file except `fradiation.json`: its path from the top of the build, its size in bytes and its SHA-256 as lowercase hex. Send the parsed `fradiation.json` as `manifest`. The site applies the same rules as the uploader and answers with the hashes it doesn't have (`missing`) and a grant for 3 hours.
2. **Upload.** For each hash in `missing`, once even if several paths share it, `PUT` the bytes to `<endpoint>/<prefix><sha256>` with the header `x-upload-grant: <grant>`. The play server refuses bytes that don't match the hash. A blob it already has answers `{ "exists": true }`.
   Files over `partBytes` (64 MB) go up in parts, at that URL:
   - `POST ?mpu=create` answers `{ "uploadId" }`, or `{ "exists": true }`.
   - `PUT ?mpu=<uploadId>&part=<n>`, for n from 1, with exactly `partBytes` bytes in every part but the last, answers `{ "partNumber", "etag" }`.
   - `POST ?mpu=<uploadId>&complete` with `{ "parts": [{ "partNumber", "etag" }] }`.
   - `DELETE ?mpu=<uploadId>` gives up on the file.
3. **finish.** The site checks that every file is there at its size, seals the build, registers mutations and scoreboards, and applies `live` and `playtest`. If a file is missing, the build fails and the message names it. Start again from begin.
4. **cancel** an upload you won't finish, so the site can clean up.

## Recipes

### With the MCP server

With the server connected, short prompts work. The agent reads what it needs with `read_doc`.

```text
Use the fradiation MCP server. Make this Unity project a Fradiation game called "<title>": create the draft, add the Unity kit, a high-score board and three mutations, build it, check it and deploy it. Make a 4:3 cover from a gameplay screenshot and upload it.
```

```text
Use the fradiation MCP server. Get the unaddressed playtest feedback for <slug>, fix the crashes and the top complaint, deploy the fix to the playtest, and mark what you fixed as addressed. Don't make anything live.
```

```text
Use the fradiation MCP server to deploy Builds/Fradiation to <slug> as a playtest build, with patch notes from the commits since the last build.
```

### Without it

Each prompt below names what the agent should read, so it works with or without the skill installed. Replace the parts in angle brackets.

### Unity build

```text
Make this Unity project build for Fradiation Games.
Read https://www.fradiation.games/docs/unity.md and https://www.fradiation.games/docs/build-rules.json.
Download https://www.fradiation.games/kits/fradiation-unity-kit.zip and unzip it into the project folder (the zip holds an Assets/ folder that merges with the project's).
Write fradiation.json in the project root, with the $schema line and a viewport that matches the game's native resolution.
Build with the batch command from the Unity page. Then run check-build.mjs (https://www.fradiation.games/skills/fradiation/check-build.mjs) on Builds/Fradiation and fix every error.
Tell me when the folder is ready to upload.
```

### Vite build

```text
Package this Vite game for upload to Fradiation Games.
Read https://www.fradiation.games/docs/web.md and https://www.fradiation.games/docs/builds.md.
Set base to "./" in the Vite config. Put fradiation.json in public/ so the build copies it to the top of dist/. Build, then run "node check-build.mjs pack dist" (https://www.fradiation.games/skills/fradiation/check-build.mjs).
Fix errors in the project, not in dist/. Give me the path of the zip.
```

### Godot export

```text
Make this Godot project export for Fradiation Games.
Read https://www.fradiation.games/docs/godot.md and https://www.fradiation.games/docs/build-rules.json.
Add a Web export preset named "Web" if there isn't one. Export with: godot --headless --export-release "Web" build/index.html
Copy fradiation.json into build/. Run check-build.mjs (https://www.fradiation.games/skills/fradiation/check-build.mjs) on build/ and fix every error.
```

### Mutations

```text
Add mutations to my Fradiation game and wire them in: <name, what earns it, tier>, <name, what earns it, tier>.
Read https://www.fradiation.games/docs/fradiation-json.md, https://www.fradiation.games/docs/sdk.md and https://www.fradiation.games/fradiation.schema.json.
Add them to fradiation.json. Take icon names only from the icon enum in the schema. Call unlockMutation with the key at the moment each one is earned. Then run check-build.mjs on the build.
```

### Scoreboard

```text
Add a scoreboard to my Fradiation game for <what is scored>.
Read https://www.fradiation.games/docs/fradiation-json.md and https://www.fradiation.games/docs/sdk.md.
Add a board to fradiation.json. Set order and format to match the score (format "time" is milliseconds) and set max to a value no honest run can pass. Call submitScore when a run ends.
```

### Playtest hooks

```text
Add playtest hooks to my Fradiation game.
Read https://www.fradiation.games/docs/playtests.md and https://www.fradiation.games/docs/sdk.md.
Call fradiation.track with short names at level start, death and win. Call fradiation.askForFeedback at game over. Do not change gameplay.
```

### Deploy to testers

Set `FRADIATION_TOKEN` in the agent's environment first, with a one-game token. Don't paste the token into the chat.

```text
Deploy this build of my Fradiation game to its playtest.
FRADIATION_TOKEN is set in the environment. Never print it, write it to a file or ask me for it.
Read the "Deploy from the command line" section of https://www.fradiation.games/docs/agents.md.
Write patch notes from the commits since the last deploy into notes.txt, one line per change.
Run: node check-build.mjs deploy <folder> --playtest --notes-file notes.txt --json
If it fails at the check stage, fix the project, rebuild and try again. For any other failure, stop and tell me the code and message.
Don't pass --live. Tell me the build id and the testers link.
```

### Ship or roll back

```text
Make build <id> of my Fradiation game live.
FRADIATION_TOKEN is set in the environment. Never print it.
Run node check-build.mjs builds --json first and confirm the build exists and still has its files, then run node check-build.mjs live <id> --json.
```

### A failing check

```text
check-build.mjs fails on my build. Run it with --json and read the errors.
Read https://www.fradiation.games/docs/builds.md and https://www.fradiation.games/docs/build-rules.json.
Fix the cause in the project or its build settings, not in the output folder. Rebuild and run the check until it exits with code 0.
```

## Batch builds

For CI, or for an agent that can't click through an editor.

### Unity

Close the project in the editor first, and install the WebGL Build Support module for the editor version the project uses. With the [Unity kit](/docs/unity) in `Assets/`:

```bash
Unity -batchmode -quit -projectPath . -buildTarget WebGL -executeMethod FradiationBuild.Build
```

`Unity` stands for the path to that editor's executable. Add `-logFile -` to print the build log to the terminal.

- The build goes to `Builds/Fradiation`. Add `-fradiationOut <folder>` to change that.
- `fradiation.json` in the project root is copied into the build.
- The process exits with code 1 when the build fails.
- The method sets the Fradiation WebGL template, Brotli compression, decompression fallback off and data caching on. Leave decompression fallback off.

### Godot

With a Web export preset named `Web` and the export templates installed:

```bash
mkdir -p build
godot --headless --export-release "Web" build/index.html
cp fradiation.json build/
node check-build.mjs pack build
```

The output name sets the names of the other files: `index.js`, `index.wasm`, `index.pck`. Keep thread support off in the preset: threaded builds don't run on the site yet. See [Godot](/docs/godot).

## Good habits

- Keep `fradiation.json` in the repo, with the `$schema` line at the top. Editors then autocomplete it, and agents stop guessing field names.
- Run the check before every upload. It takes seconds, and `deploy` runs it for you.
- Write patch notes in the commit message, then pass them with `--notes-file` or paste them into the uploader. They can be up to 2,000 characters and show in the game's update log. An agent can draft them from `git log` since your last upload.
- Don't let agents invent mutation icon names. The `icon` field takes one of the values in the schema's enum, and the server refuses anything else.
- Fix problems in the project, not in the output folder. The next build overwrites the output.
- Send agent deploys to testers (`--playtest`) and make them live yourself, or say so explicitly. A released game's players get a live build on their next load.
- Give each agent or CI job its own one-game token with a short expiry, rather than your signed-in browser. Revoke tokens you stop using.
