Set one environment variable and the ARC-AGI-3 toolkit stops talking to the internet. OPERATION_MODE=offline brought the constructor time on a studio test machine from 1.21 seconds to 0.00, requested no API key, and played ls20 from the copy already on disk.
ARC-AGI-3 offline mode is the least documented and most useful switch in the toolkit, and the source explains it better than the docs do.
What the four ARC-AGI-3 operation modes actually do
arc_agi/base.py defines an OperationMode enum with four values, and the comments on it are the closest thing to a specification:
NORMAL Use both local environments and API (default)
ONLINE Use only remote environments via API
OFFLINE Use only locally available environments
COMPETITION (API only, plus competition scoring)The difference shows up in make(). In OFFLINE it calls _find_local_game, which only searches what has already been scanned from disk. In NORMAL it calls _download_game, which fetches the source and then runs it locally, so NORMAL is really “download once, then offline”. In ONLINE it builds a remote wrapper and every step is an HTTP call to three.arcprize.org. COMPETITION is the online path with the rules of the leaderboard attached. Its documentation says it is “REQUIRED to show up on the Unverified leaderboard”, and it forces the behaviour a fair run needs: environments only through the API, one make() per environment, level resets only, a single scorecard, and scoring “against all available environments, even if you choose not to interact with them”.
The precedence is worth writing down because it has one deliberate exception. Constructor argument beats environment variable beats the default of NORMAL, except that OPERATION_MODE=competition in the environment beats a constructor argument. The code comments say why: in the competition harness the organiser’s setting has to win over anything a submission passes in.
How to run ARC-AGI-3 offline mode with no API key
Two things have to be true. The game has to be on disk, and the mode has to be OFFLINE.
Getting the game on disk is one NORMAL run. The toolkit’s own quickstart says an anonymous key is used if you provide none, and that is what happens: Got anonymous API key, a fetch of the 25 public environments, a download to environment_files/ls20/9607627b/. That first run is covered in full elsewhere; it took about two and a half seconds.
After that:
OPERATION_MODE=offline python3 agent.pyor in code, arc_agi.Arcade(operation_mode=OperationMode.OFFLINE). On this machine the constructor then reported key: '', listed ['ls20-9607627b', 'bp35-0a0ad940'] as the available environments, and took 0.00 seconds. make("ls20") logged Found latest version of ls20: ls20-9607627b, loaded the class from the local file, and the first RESET returned a normal frame with state NOT_FINISHED.
Ask for a game you do not have and it fails cleanly: Game zz99 not found in scanned environments. Available games: ['bp35', 'ls20']. Nothing is downloaded on your behalf in this mode, which is the point.
What OFFLINE skips is visible in the constructor. In any other mode, an empty key triggers one unauthenticated GET to /api/games/anonkey on three.arcprize.org, and the UUID that comes back is sent as an X-API-Key header from then on. Then _fetch_from_api pulls the environment list, which is where the 1.21 seconds went on the first run here. In OFFLINE both calls are skipped, and the only work the constructor does is _scan_for_environments, which walks the environments directory looking for metadata.json files and records each one’s parent directory as the place to load the game from. That scan is the whole reason a fresh offline constructor finishes in 0.00 seconds: it is reading two small JSON files.
Where the games live is also configurable. The default directory is environment_files under the working directory, and ENVIRONMENTS_DIR overrides it, so a shared cache across projects is one variable.
Which ARC-AGI-3 version a game runs, and how to pin it
Every downloaded game sits under a version hash, and the id the toolkit reports is ls20-9607627b, not ls20. That matters for reproducibility. make("ls20") picks the latest version on disk. make("ls20-9607627b") pins one: make() splits on the hyphen and passes the version through to the local finder. If a game is revised upstream, an offline run keeps playing the version it has, and a NORMAL run will fetch the new one alongside it rather than over it.
The metadata for each version records date_downloaded, and the human baseline for every level, so a pinned run also pins the numbers it is scored against. That is worth more than it sounds. A benchmark number quoted without a game version is a number that cannot be reproduced once the game is revised, and the toolkit gives you the version for free in every log line.
What still happens locally in every mode
Scorecards are the part people assume needs the server. They do not. The toolkit runs a ScorecardManager in every mode, and its first log line on startup gives the two timers: a scorecard goes idle after 15 minutes and is force closed after 3 days, both overridable with STALE_MINUTES and MAX_OPEN_FOR_MINUTES. In OFFLINE and NORMAL the scorecard is computed locally with the same RHAE code the leaderboard describes, so you get a per level score without an account.
Recording is local too. Pass save_recording=True to make() and every frame and action goes to a JSONL file under recordings, or under whatever directory you pass as recordings_dir.
One engine level variable is worth knowing while you are here. ONLY_RESET_LEVELS=true changes what RESET does: normally the first reset of a game, or a reset after a win, reloads every level from clean, while any later reset only reloads the current level. With the variable set, every reset is a level reset unless the game has been won. Agents that rely on levels_completed should know which behaviour they are getting.
When offline mode is the wrong choice
OFFLINE gives you the 25 public games and nothing else, forever. The technical report is explicit that the public set is the easiest layer and “does not comprehensively represent the mechanics found in the private” sets. Anything you measure offline is a measurement of your harness on a development set.
ONLINE is for when you want your run to appear on a scorecard the server holds, or when you are testing against the API path a competition submission will use. Even then the games it can reach are the same 25. The semi private and fully private sets are never served to an anonymous key, and are never served by this code path at all; they are run by ARC Prize against systems it evaluates.
So the honest description of the toolkit’s modes is this. OFFLINE and NORMAL are for building. ONLINE is for checking that what you built survives the network. None of them are for finding out how good your agent really is, because the games that decide that are not on anyone’s disk but theirs.
