Checks Axiom plugins, game providers and language providers against the Axiom Style Guide and reports every finding with its rule ID, linked to the rule:
axiom.toml:14:1: MUST AXIOM-06.2: game API module mygame: ">=2.1" has no upper bound; add the next major (<N) [chopper]
src/lib.rs:42:9: MUST NOT AXIOM-12.3: `.unwrap` can panic and disable the plugin; return the error (?) or use a default [chopper]
It finds every axiom.toml under the paths it is given (or above them), works out whether each project is a plugin, a game provider or a language provider, and runs:
- The manifest checks: required fields, ids and their shapes, semantic versions, version ranges with a next-major upper bound, the
[load]phase, capabilities with reasons for the optional ones,[network] hostsfornet:connect,process:nativefor .NET and native plugins, and no secrets or machine-specific paths. For game providers: exact builds, signature files (Signatures/<build>.toml), the module spec and an example plugin; for language providers: the pinned engine. - The project checks: tests beside the code (
<file>.test.<ext>), a lifecycle test and a denied-capability test, the conformance suite config, CHANGELOG.md, the licence file, the Axiom SDK pinned exactly and lock files committed, and a README saying why a plugin is above tier 1. - The source checks, comment- and string-aware, in Rust, C#, Lua, C and C++: logging through Axiom, no panics, exits, raw threads or sleeps in plugins, the C ABI rules (
struct_size, zero-initialised structs,AX_STR, fixed-width types,AX_CALLandnoexcept, checked results and API versions), ownership (ax_alloc/ax_free), loader-lock and process-state rules, private detour libraries, hard-coded addresses, inline byte patterns, and capabilities the code uses but the manifest does not declare.checks.jsonlists them all. - The formatters, when installed: clang-format with the project's
.clang-format, rustfmt (pluscargo clippy), StyLua with the project'sstylua.toml, anddotnet format. Files with no.clang-formatorstylua.tomlabove them are not format-checked (the formatters' defaults are not the guide's style); a note says so.dotnet formatchecks style and analyzers once the solution is restored (dotnet restore), and whitespace only before that.
Lua build scripts (premake5.lua, Dependencies.lua) are not plugin code and are skipped. Generated files (GENERATED FILE: DO NOT EDIT, Generated/ folders) are skipped too: change the spec and regenerate instead. Plugins and providers in C or C++ also follow the C++ Style Guide; run its kit for clang-tidy and its own rules.
Run it
Needs Python 3.9+ (3.11+ reads TOML with tomllib; older versions use a small built-in reader). The tools are optional: clang-format 19 (pip install clang-format~=19.1.0), rustup with rustfmt and clippy, StyLua, and the .NET SDK.
python3 chopper_lint.py # the current folder
python3 chopper_lint.py plugins/minimap # one project in a monorepo
python3 chopper_lint.py --no-clippy # skip cargo clippy (it builds the crate)
python3 chopper_lint.py --no-format --strict # the guide's own checks only; fail on SHOULD too
python3 chopper_lint.py --list-checks # what it checks
Folders named vendor, third_party, external, build, bin, bin-int, obj and target are skipped. Whether a lock file is committed is checked in a git work tree only: run cargo generate-lockfile or dotnet restore and commit Cargo.lock / packages.lock.json after starting from a template.
Exit status: 1 when a MUST or MUST NOT rule is broken (any finding with --strict), else 0.
In GitHub Actions
- uses: actions/checkout@v4
- uses: carson-hopper/chopper.dev/content/styleguides/axiom/lint@main
with:
paths: .
# formatters: "false" # the guide's own checks only
# clippy: "false" # skip cargo clippy
The action looks at which languages are present and installs only what they need: clang-format for C and C++, StyLua for Lua, the toolchain your rust-toolchain.toml pins for Rust, and the SDK your global.json pins for C#. Findings appear as annotations on the pull request. Pin @main to a commit SHA for reproducible runs.
Silencing a finding
On the same line or the line above, with a reason, in the file's own comment syntax:
let config = parse(text).expect("checked above"); // chopper-lint: allow AXIOM-12.3 (validated in on_load)
"legacy.waypoints" = "*" # chopper-lint: allow AXIOM-06.2 (unversioned internal plugin)
For a whole file: // chopper-lint: allow-file AXIOM-12.3 (-- in Lua, # in TOML, <!-- --> in .csproj and .props files). Findings about a whole project, such as a missing CHANGELOG.md or missing tests, are reported on its axiom.toml, so an allow-file there silences them.