Builder docs

Driving an AI agent

The literal opening prompt to paste at the start of the conversation, one per architecture.

Most people building here drive an AI agent. This page is the literal text to paste at the start of that conversation — one prompt per architecture. Copy the whole block, replace the one angle-bracket line describing your game, and send it.

These are the only copies. If you find this prompt reproduced somewhere else, the copy here is the one that is maintained.

Which prompt

Decided by where your game's state lives, not by how fast it plays.

Your gamePrompt
Two to four people playing each other, moves signed off chainA — Arcade / channel game
A world the chain persists, every move its own transactionB — persistent on-chain world

Unsure? Read /skills first — it is one screen and it will route you.

Prompt A — Arcade / channel game

I want to build a multiplayer game for the Xaya Arcade. Before you write any code:

1. Get the Arcade skills if you do not already have them. Take the zip:
     curl -fsSLO https://arcade.xaya.io/skills/arcade-skills.zip
     unzip arcade-skills.zip -d .claude/skills/
   If a template fork is already open in this workspace it carries the skills
   vendored at .claude/skills/ — use those and install nothing.
   No .claude/skills/ support? Read them straight from the site — the same files:
     https://arcade.xaya.io/skills/building-channel-games.md
     https://arcade.xaya.io/skills/building-channel-games/arcade.md
     https://arcade.xaya.io/skills/building-channel-games/standalone.md
     https://arcade.xaya.io/skills/building-channel-games/wasm.md
     https://arcade.xaya.io/skills/building-channel-games/wagering.md
     https://arcade.xaya.io/skills/building-channel-games/hidden-information.md
     https://arcade.xaya.io/skills/building-channel-games/commit-reveal.md
     https://arcade.xaya.io/skills/building-channel-games/pitfalls.md
     https://arcade.xaya.io/skills/building-channel-games/examples.md
   Once xaya/arcade-skills is public, the Claude Code marketplace pair installs
   the same corpus:
     /plugin marketplace add xaya/arcade-skills
     /plugin install arcade-skills@arcade-skills

2. Read on this schedule, and do not skim:
   - ARCADE.md in full, BEFORE any design;
   - WASM.md before you touch rules/ or blob/ — that is where the blob toolchain
     and golden-trace authoring live;
   - PITFALLS.md before you submit;
   - https://arcade.xaya.io/docs/quickstart.md and
     https://arcade.xaya.io/docs/submitting.md in full.
   Do not improvise anything these cover from memory.

3. Decide the two PERMANENT names first, before any design or code: slug and
   gameType. They are claimed at upload and registered on chain when the game is
   accepted: gameType can never be reassigned to another game, ever, and slug can
   never be reused. There is no availability endpoint — you find out when you
   upload — so the safe order is to dry-run on the playground (step 6), which
   auto-accepts and tells you in seconds, and keep the resubmitToken it returns.
   Nothing is claimed until upload and nothing is burned until an operator
   accepts; a rejected submission releases both names. Only acceptance is
   terminal.
   Seats and the cfg suffix are PROVISIONAL — revise them freely while you build,
   because the cfg suffix is derived from rules that do not exist yet. Keep them
   in step with GAME_KEY in src/app-identity.ts, the blob's seat range and the
   rules' cfg decoder as they settle, and confirm all four with me before we
   submit.

4. Clone the template:
     git clone https://github.com/xaya/arcade-xayaman.git
   If any git clone in these instructions fails, STOP and tell me. Do not
   reconstruct the judge ABI, the SDK surface or the submission contract from
   memory.

My game: <one paragraph — what the players do, how a match ends, how many seats>

Non-negotiable constraints:
- The rules are a deterministic wasm judge against the frozen arcade_* ABI, whose
  authority is docs/ARCADE-ABI.md in arcade-platform — read it, never reconstruct
  it. Never add an import, never invent an export, no floats, no wall-clock, no
  randomness outside the channel seed.
- The frontend never holds a wallet and never signs. Inside the Arcade the shell
  is the only signer; the SDK's standalone wallet path exists for local
  development only, so never write wallet, connect or signing code in the game.
- Free play first. Wagering is a separate operator action and is not part of this.
- The game is served under a base path, from ONE origin shared with every other
  game and with the shell. So: never construct a root-absolute URL at runtime or
  write one as a literal — no fetch('/data.json'), no <img src="/sprite.png">, no
  url(/bg.png) — and set STORAGE_PREFIX in src/app-identity.ts to our own game's
  key, never the template's.
- An Arcade game is 2 to 4 seats, React/Next only (the SDK has no supported
  non-React path today), and ships as a static export with no server of any kind —
  no API routes, no server actions, no database, no asset backend, no leaderboard
  service, and no accounts beyond your Xaya p/ name. All shared state is produced
  by a deterministic wasm judge with no floats, no clock, no network and no file
  access, whose state output is bounded and whose execution is fuel-capped — which
  caps how big a world and how long a history you can have. Input is keyboard- or
  tap-shaped, and every game must be playable by touch: it declares on-screen touch
  controls (a d-pad and/or action buttons) that the SDK mounts on touch devices, or
  it declares that it draws its own touch surface and takes the whole frame to draw
  it in — a game that declares neither is labelled desktop-only on the shelf and a
  player on a phone cannot start or join a match in it. There is no gamepad support
  and no key remapping. And a
  match needs two to four named, funded humans online at the same moment; there is
  no solo mode, no bot and no matchmaking.
  If my idea does not fit, say so NOW rather than working around it.
- There is no self-serve update path: after acceptance the slug and gameType are
  single-claim and the gameType is on chain, so a fix costs a new slug and a new
  gameType. A REJECTED submission releases both — only acceptance is terminal.
- Build the submission bundle only with ./scripts/build-export.sh --bundle.
- Work test-first. Before we submit, all of these must be green and you must show
  me the output: npm test; bash blob/tests/run-tests.sh; npm run e2e;
  npm run e2e:determinism; npm run selfplay; bash blob/check-blob.sh. These run
  offline; there is no local chain, so none of them opens a real channel.
- Then DRY-RUN THE WHOLE PIPELINE on the public playground before submitting for
  real. It is a disposable test chain, it auto-accepts, and it is wiped every
  Monday 04:10 UTC and without notice in between:
    a) point the dev server at it with the committed script:  npm run dev:playground
       That sets all FIVE values the loop needs — WS_PROXY_DESTINATION,
       CHAIN_PROXY_DESTINATION and GSP_PROXY_DESTINATION at test-arcade.xaya.io,
       plus NEXT_PUBLIC_POLYGON_RPC=/chain and NEXT_PUBLIC_DEV_WALLET=1. Without
       the last two the browser reads REAL Polygon and you have no test wallet.
       Never put them in .env.local: Next loads that for builds too and they
       would be baked into the bundle we submit. You should see a dev-wallet
       picker, not a wallet-connect prompt — if you see a connect prompt, the
       env did not apply, so stop and tell me.
    b) NEXT_PUBLIC_GAME_ID=xarc ./scripts/build-export.sh --bundle, then upload
       at https://test-arcade.xaya.io/attach — that is the playground's upload
       page. Once it auto-accepts, the game is live there twice over: bare at
       /g/<slug>/, and inside that plane's arcade shell at /play/<slug>.
       (xarc is the playground's shared move namespace, NOT our game key — a
       bundle for arcade.xaya.io is built with arcbeta instead; the bundle
       refuses to boot on a mismatch. Keep the resubmitToken the upload returns —
       you need it to re-upload the same slug after a fix.)
    c) when it auto-accepts, open the game in TWO BROWSER PROFILES — one per seat
       if the game seats more than two — and pick a DIFFERENT test-wallet slot in
       each, then reload. Two tabs of one profile are the same player. Play a
       full match.
  Submissions on arcade.xaya.io are closed during the curated phase: /submit is
  hidden there and an upload answers 503. So hand the SAME BYTES over in
  #builders on the Xaya Discord, with the repository and the four registration
  values (slug, gameType, seats, cfgSuffix). Check them:
  sha256sum dist/bundle.tar.gz blob/rules.wasm must match the bundleSha256 and
  rulesSha256 the playground echoed back in its 201 upload response (also at
  GET https://test-arcade.xaya.io/api/submissions/<id>) — no page prints them, so
  capture them from the response. If they differ, we are submitting bytes we never
  dry-ran — rebuild and redo the dry run.
- The playground reserves NOTHING. It is a different chain, wiped at will. A game
  that works perfectly there can still lose its gameType to someone else, and a
  playground success is not a reservation of anything on the real arcade.
- Upload the blob whose sha256 the green run used, and the bundle built from that
  same commit. The browser loads the REGISTERED blob, so a bundle built against a
  different one boots, joins, and then renders garbage.

Give me a plan and wait for my approval before writing code. Ask one question at a
time.

Prompt B — persistent on-chain world

I want to build a Xaya game whose state lives fully on chain — a persistent world
where every move is its own transaction, not a game channel. Before you write any
code:

1. Get the Xaya skills if you do not already have them. Take the zip:
     curl -fsSLO https://arcade.xaya.io/skills/arcade-skills.zip
     unzip arcade-skills.zip -d .claude/skills/
   No .claude/skills/ support? Read them straight from the site — the same files:
     https://arcade.xaya.io/skills/building-persistent-games.md
     https://arcade.xaya.io/skills/building-persistent-games/gsp.md
     https://arcade.xaya.io/skills/building-persistent-games/determinism.md
     https://arcade.xaya.io/skills/building-persistent-games/ops.md
     https://arcade.xaya.io/skills/building-persistent-games/fork-testing.md
     https://arcade.xaya.io/skills/building-persistent-games/pitfalls.md
     https://arcade.xaya.io/skills/building-persistent-games/examples.md

2. Read SKILL.md and GSP.md in full before any design, DETERMINISM.md before you
   write a line of game logic, and FORK-TESTING.md before you run anything against
   a chain. Do not improvise anything they cover from memory.

3. Clone the foundation and the worked example:
     git clone https://github.com/xaya/libxayagame.git
   The mover/ directory inside it is the smallest complete game-state processor
   there is — scaffold from it rather than from a blank file. If any git clone
   fails, STOP and tell me.

My game: <one paragraph — what the world is, what a player does in one move, and
what the state has to remember between moves>

Non-negotiable constraints:
- The GSP is the only authority on state. State is a pure function of the ordered
  move sequence and nothing else — no wall-clock, no randomness that is not
  derived from chain data, no floats, no map or set iteration order dependence,
  no uninitialised memory, no locale. Two independently synced GSPs must return
  byte-identical gamestate; that is the acceptance test, not an aspiration.
- Pick the genesis height deliberately and tell me what you picked. Moves before
  it are invisible forever, and a height above the current tip never syncs.
- Every move is a real on-chain transaction the player pays for. Say out loud, in
  the design, what one move costs a player and how often they will send one — a
  world that needs a move per second is not this architecture.
- Names are permanent and are ERC-721 tokens on Polygon. Registration costs WCHI.
- Test against a FORKED Polygon chain, never mainnet and never a testnet.
  FORK-TESTING.md has the procedure and the traps — including that a fork's state
  is memory-only, so restarting the container is a wipe rather than a hiccup.
- Work test-first: a golden-replay suite and a reorg suite, both baked into the
  image build as a deploy gate, before the game is ever run anywhere real.

Give me a plan and wait for my approval before writing code. Ask one question at a
time.

After the prompt

The agent will ask for a plan approval. Read it. The two things worth checking hardest, because they are the two that cannot be undone later:

  1. Does the game actually fit? Seats, no server, input that works from a keyboard or the SDK's on-screen pad (no gamepad, no remapping), humans online at the same time. An agent that has decided to build your idea will find a way to describe it as fitting.
  2. Are the slug and gameType the ones you want forever? gameType goes on chain at acceptance and can never be reassigned. Sleep on it.

Everything else is recoverable.