/examples/api-worlds-ci.yml.
What the workflow needs
Three repository secrets, and nothing else. The keys decide which project the worlds are read from, so a CI key pointed at a staging project keeps pull requests away from production data.
The
gateway CLI reads all three straight from the environment, so no gateway auth login step is needed. Install it with npm install -g @withgateway/sdk; it needs Node 18 or newer.
GATEWAY_HOST is not secret, and a repository variable (vars.GATEWAY_HOST) works just as well. Keeping all three together as secrets keeps the workflow short.The workflow
The tests job checks the worlds
gateway worlds test <slug> runs the suite the world’s head version ships — the tests/*.json cases and tests/test_*.py files described in Test a world — on the platform, and exits 1 when any case fails.
One matrix entry per world means a broken world fails its own job under its own name, and fail-fast: false lets the rest finish, so one run reports every failure.
The argument is a slug, not a path. If a directory of the same name sits in the working directory, the CLI tests that directory instead. Keep matrix values distinct from your folder names, or pass
./worlds/<dir> deliberately.The smoke job checks the agent
One session, one agent, one grade. The session is opened on the world’sapi surface, the agent is pointed at api.url with the session token, and the task’s own grader scores the end state.
Four details in that job matter.
Open with --no-wait and poll. --no-wait prints the session id as soon as the platform accepts the open, and the step polls gateway worlds session status until ready is true.
Mask the token. The session token gates the api surface, so ::add-mask:: it before it can reach a log line, and send it as an Authorization: Bearer header — never in a URL.
Gate on a number. gateway worlds session grade prints {sessionId, reward, rewards, raw}; jq -e turns the reward into the step’s exit code so a weak run fails the job rather than passing quietly.
Close in an always() step. A session that no job closes sits there until it closes for being idle. Closing it in always() returns the container whether the agent passed, failed, or crashed.
Your agent reads two variables and nothing else:
Check the pull request’s own tree before it is pushed
Both jobs above read published versions. A pull request that edits a world’s contract, handlers or cases has not published anything yet, so add a job that tests the tree in the checkout:python3 of 3.12 or newer on the runner, the CLI serves the world from its bundled runtime and runs everything locally — no push, no version, no session. Install pytest alongside it, or the Python half of the suite fails and the python tests line names the install command.
Push on merge, and let the push run the tests
Publish on merge, not on every pull request. A world whosegateway-env.toml carries [actions] on_push = ["tests"] gets its suite run against each pushed version automatically, so the merge workflow only has to push:
gateway bench tests acme-ledger.
The on-push run sees the pushed tree and nothing else, so cases that count rows fail on a version whose data has not been published yet. Test a world covers how to keep JSON cases bare-tree-safe.
One command instead of three
gateway worlds ci is the whole smoke job in one line. It pushes the checked-out world, runs every task, writes a summary table into $GITHUB_STEP_SUMMARY, and exits 1 under the score you set:
GITHUB_HEAD_REF on a pull request, so each pull request’s runs are tagged with its own branch. Use the longer form when you need the session, the URL, or the token yourself.
The same gate from TypeScript
When the pull request changes the world’s own source,validateWorld does it in one call from Node: it pushes the checkout on the pull request’s branch, dispatches a named run config against that exact version, waits for every run, gates on the score, and returns a comment-ready markdown summary.
runSessions opens one container per scenario, runs your agent against each, and gates the mean reward. Both are covered in Gate a pull request and Run a task suite.
Where to go next
- Test a world for the case format and what the report says.
- The sample repository for a working repository that runs this loop across three worlds.
- Spin worlds up and down for the session lifecycle.
- Run many sessions at once for running a whole scenario set instead of one smoke run.