Skip to main content

Module contract

Module contract 

Source
Expand description

The tool contract (ADR-0005 §1, ADR-0022): the operations an agent, CI or a person runs on a game, each a library function with a typed input and output that serde reads and writes and JSON schemas describe. The CLI (shuyi <op> [--json]) is one surface over them; the MCP server (shuyi mcp, crate::mcp) is another, where each is a tool and a session keeps one game running between calls.

OpDoesModule
new <dir>a new game from the engine’s template, templates/shuyi-default, its parts picked by the answers (--template grid, --answers, --set)new
check [--baked]every error of every validator of the game’s content; with --baked, every way its pack differs from its sourcescheck
check --target <id>whether the game’s frame, text and controls fit a targetcheck, target
lintthe game’s code: check-keys, check-text and the rules crate’s fence, each finding with file and linelint
modulesthe catalog by concern, the modules the game’s engine builds, and the ones the game has onmodules
add <module>a module turned on: its feature of shuyi in Cargo.toml, its skill copied, what it is for, an alternative already onadd
runplays a key script headless: the top screen, the scene, events, sounds; with --perf, the frames’ timesrun, perf
scenethe scene or stage on screen after a script; with --path one value of its data, with --shape its shaperun
replay <record>a state hash per step, and the first step where two runs differreplay
playtestthe game’s named bots each play K tries on every core, timed and judged against its bands (none: a plain bot run); each try’s commands and record for replay, each bot’s outcomes counted and the spread of its numbersplaytest
autobalancethe game’s knobs change a stage until its named bots land in their bands: the changes, the reports before and after; with --apply, the stage writtenautobalance
import <file>a file read through the format registry, or its errorsimport
bake [--target <id>]the game’s sources baked into one pack: sizes and timesbake
export <target>the target’s check, then the game built and packaged for it: a zip, a tar.gz, an .app or a web folder, with NOTICES.txt, export-report.json and exposure-report.jsonexport
doctorthe toolchain: what is missing and how to install itdoctor
explain [<code>]every problem code, or one code’s pageexplain
attach <host:port>a running debug build over the live link: steps played as a player’s input, then its screens, frame, log and window’s pictureattach

Every answer is an Answer: the contract’s VERSION, the operation, whether it passed, its result or the error that stopped it, and its limits, what it could not determine (“no scene: the top screen is no stage”). An error carries a problem code and a help line (codes), and the code’s class sets the exit code: 0 passed, 1 failed, 2 wrong arguments, 3 fix the setup, then run it again.

The MCP server’s session tools (start, step, scene, frame_png, events, perf, reset, stop; Op::SESSION) answer the same way.

The schemas of every input and answer are generated from these types (schemas) and checked in under crates/cli/schemas/. A test fails when they change and VERSION doesn’t.

Modules§

add
add <module>: a module of the game’s engine turned on (ADR-0009 §6). It:
attach
attach: a build that is already running, reached over the live link (ADR-0047) instead of a game started headless. It needs no project: the build is a debug build of a game with its backend’s link feature, started with SHUYI_LINK=<port>, and its log’s line shuyi link: listening on <address>, token <token> gives the address and the token.
autobalance
autobalance: the game’s knobs change one of its stages until each of its named bots (its entrants) succeeds as often as its checks’ bands want (shuyi_core::autobalance_by). The bots never change, only the stage. The game gives the plan (GameTools::autobalance_plan): its entrants and checks, as playtest has them, and the stage the search may change, with its knobs (Tunable). This runs the search, each setting’s tries played on every core (crate::pool) and judged once in seed order, so what it finds never depends on the thread count. It stops at the budget of evaluations or the time limit with the best setting seen, says the changes in the knobs’ words and, with apply, has the game write the changed stage. The answer passes when every rate is in its band.
bake
bake [--target <id>] [--out <file>] [--sheet <file>]: the game’s sources baked into one pack (.shuyipak, ADR-0023), with what went in, the sizes, and how long baking and loading took. A source with errors is refused, with every error. check --baked uses the same bake to prove the pack equal to its sources (check_baked).
check
check [--baked]: the game loads all its content through every validator it has, and the answer lists every error found, each with its file, line and message. With --baked, the game’s sources are baked into a pack too, and every asset read from the pack must equal the one read from its source (bake::check_baked). Once the content loads without errors, the game’s text is checked against its font atlas (GameTools::font_atlas, shuyi_assets::font::undrawn): a character the atlas can’t draw is an error. It passes when there are no errors.
cli
The contract on the command line: shuyi <op> [args] [--json]. Each op’s arguments become its input, its answer is printed for people or, with --json, as one JSON document (Answer::to_json). An error carries a problem code (codes), and the exit code is 0 if it passed, 1 if not, 2 for wrong arguments (SH0nnn), and 3 for a setup to fix before running it again (SH1nnn: no project, a project file that lacks what it needs, the toolchain).
codes
Problem codes (ADR-0028): every error the contract gives carries a stable code, SH and four digits, whose first digit is its class, and the class sets the exit code. Each code has a row here, with a help line, and a page in crates/cli/errors/ that shuyi explain <code> prints.
doctor
doctor: checks the toolchain a game on the engine builds with: Rust and Cargo, rustup’s targets, MSYS2’s linker and objcopy on the Windows GNU toolchain (AGENTS.md), on Linux the C linker, pkg-config and the system libraries the backend links and opens, and the web target. Each check says what it found and, if it failed, how to fix it (its help): on Linux, the install command for the distribution’s package manager (/etc/os-release), and scripts/setup-linux.sh, which installs the same list. It passes when every check does; when one fails, it answers codes::TOOLCHAIN and exits 3.
explain
explain [<code>]: every problem code, or one code’s page (codes). It needs no project, like doctor and schema, and is how an agent or a person learns what an error’s code means and how to fix it (shuyi explain SH2003, as rustc --explain E0277).
export
export <target> [--out <dir>] [--release] [--run] [--without <feature>]: the game checked against the target (check --target with the target’s profile: the desktop one for windows, linux and macos), refused on any error, then built and packaged by the target’s exporter (crate::export). The package goes in --out (from the game’s folder; dist/export/<target> without it) with export-report.json, which holds this answer’s result: the target, the version, the sizes, the check’s warnings, the signing and, with --release, the debug file kept beside the package. The final package is read back into exposure-report.json, and refused if it breaks the game’s disclosure part (crate::export::disclosure). With --run, the package is run after that.
import
import <file> [--kind K] [--print]: a file read through the format registry (ADR-0019) as every kind whose formats know it, with a short summary of what was read, the asset itself with --print, or every error with its file and line.
lint
lint: everything wrong with the shape of the game’s code, in one answer, each finding with its file, its line and what to do (ADR-0005 §1):
modules
modules: the catalog by concern (ADR-0009 §6), as the engine the game builds with has it, and which modules the game has on.
new
new <dir>: a new game in dir, from the engine’s template, templates/shuyi-default, made of parts (ADR-0013 §3, crate::template). It is embedded in the shuyi binary, so new needs no checkout of the engine.
perf
Perf traces as the contract hands them to agents (ADR-0046 §7): the frames’ records a session kept, read as a stream (PerfRead), summed up (summary), and written as JSON lines (lines).
playtest
playtest: the game’s named bots (its entrants) each play its stage K times from seeds seed, seed + 1, …, the game measures every try, and its checks say per bot whether it succeeded as often as wanted (shuyi_core::bots::playtest). The game gives the plan (GameTools::playtest_plan): its stages, its entrants and their groups, its checks and how one try is played. This plays the tries on every core (crate::pool), times them, and judges them once in seed order, so the report never depends on the thread count. The answer passes unless a rate fell under its floor.
replay
replay: a record of a game re-run by the game’s own rules, with a state hash per step (shuyi_core::state_hash). One record is run twice, which finds luck or state that a run doesn’t own; two records are run side by side. The answer says the first step where the two runs’ hashes differ, and passes if there is none.
run
run and scene: the game started headless as the game’s options say, a script of keys, controller buttons, actions by name and waits played, and some frames more. run answers with the top screen, the frame as text, the scene, what happened, the sounds asked for, the game’s log and its modules; scene with the scene and the log. With --perf, run also measures each frame and answers a summary of the times, and with --perf-out writes every kept frame as a JSON line (super::perf); where the heap is counted by memory tag, it takes a heap snapshot before the script and one after the last frame, and answers what held the heap and what grew. With --session, the game starts from a session record instead (shuyi_platform::session), played to its end or to the first frame this build plays differently, and the script goes on from there.
session
A game running headless, for the operations that play it (run, scene) and the MCP server’s session (crate::mcp): Session, which any game gives as a Headless over its Harness.
target
check --target <id>: whether the game fits a target (ADR-0008 §1), by the target’s profile and the game’s setting for it in shuyi.ron (TargetSetting). Five checks for now:

Structs§

Answer
What an operation answers.
Problem
A problem in a file: what a validator found, with where.
SchemaFile
One schema file: its name (run.input.json) and its text.
VersionLine
One line of schemas/versions.json: a version and its schemas’ fingerprint.

Enums§

Op
An operation of the contract.

Constants§

VERSION
The contract’s version, in every answer. Raise it whenever an input or an answer changes shape (the schemas’ test says when), and add the new schemas’ fingerprint to schemas/versions.json.

Functions§

fingerprint
The fingerprint of files: a hash of their names and texts, which schemas/versions.json keeps for each VERSION.
op_schemas
The schemas of op’s input and answer: as the CLI and the MCP tool of its name take it, or, if session, as the MCP server’s session tool does (named session.<op>; Op::SESSION).
schemas
Every operation’s input and output schema, in Op::ALL’s order, then the MCP server’s session tools’ (Op::SESSION), then the file export writes beside its package.