# Axiom Style Guide: rules digest

> Every rule from the Axiom Style Guide (rev. 2026.10), without the prose, tables and examples. The full guide: https://style.chopper.dev/axiom@2026.10/llms.txt

Revision 2026.10, released 2026-10-19. This pinned URL never changes; the latest text is at https://style.chopper.dev/axiom/rules.md. Changelog: https://style.chopper.dev/axiom/changelog.md.

MUST / MUST NOT are non-negotiable. SHOULD / SHOULD NOT are the default; deviate only with a short comment saying why. MAY is allowed when it helps. AVOID is allowed but a smell. Cite a rule by its ID (AXIOM-02.1): IDs never change or get reused, and https://style.chopper.dev/axiom@2026.10#AXIOM-02.1 links to the rule. Code examples use a fictional game, `mygame`, and a fictional author, `acme`. Plugins and providers written in C or C++ also follow the C++ Style Guide (https://style.chopper.dev/cpp). This guide only adds what is specific to Axiom.

## Starter project

A starter project that follows this guide exists, but templates are not versioned: see the latest guide at https://style.chopper.dev/axiom/llms.txt for its download URL and parameters.

## 02. How Axiom fits together

- `AXIOM-02.1` **MUST** keep dependencies pointing down the diagram: plugins → language providers → ABI → core → game providers.
- `AXIOM-02.2` **MUST** reach the game from a plugin only through the Axiom API and a game API module. There is no back door for "trusted" native plugins.
- `AXIOM-02.3` **MUST** add support for a game by writing a game provider, and for a language by writing a language provider. Neither requires changes to the core.

## 03. Choosing what to build

- `AXIOM-03.1` **MUST** start every new plugin on tier 1 and move to a higher tier only for a reason written in the plugin's README.
- `AXIOM-03.2` **MUST NOT** write a game provider to work around a missing API; ask for (or contribute) the API in the game's API module instead.
- `AXIOM-03.3` **SHOULD** split a large plugin into a library plugin and thin feature plugins when other authors could reuse the library.

## 04. Toolchain and project layout

- `AXIOM-04.1` **MUST** keep one plugin or provider per repository (or per top-level folder in a monorepo), with its manifest at the root.
- `AXIOM-04.2` **MUST** keep tests beside the code they test (`<file>.test.<ext>`), and shared fixtures in `tests/`.
- `AXIOM-04.3` **MUST** commit the lock file of the language's package manager, and pin the Axiom SDK version.
- `AXIOM-04.4` **MUST NOT** vendor or modify Axiom headers; depend on the SDK package.
- `AXIOM-04.5` **SHOULD** keep game-build-specific data (signatures, offsets) in data files under `Signatures/`, not inline in code, so a game update is a data change.

## 05. Naming

- `AXIOM-05.1` **MUST** choose a plugin ID once; it is how saves, configs, dependencies and the registry find the plugin. [MUST NOT] rename it after the first release.
- `AXIOM-05.2` **MUST NOT** use the `ax_`, `AX_`, `axiom.` or `provider.` prefixes for your own symbols or IDs; they are reserved for Axiom and official providers.
- `AXIOM-05.3` **MUST** namespace every event, config key and storage key a plugin defines under its plugin ID.
- `AXIOM-05.4` **MUST** keep IDs, event names and capability names lower-case ASCII so they are stable across file systems and languages.
- `AXIOM-05.5` **SHOULD** use Axiom's nouns consistently in code, docs and UI: *plugin*, *game provider*, *language provider*, *game API module*, *event*, *hook*, *capability*.

## 06. The plugin manifest

- `AXIOM-06.1` **MUST** declare every capability the plugin uses, with a one-sentence reason for each optional one. Undeclared calls fail with `AX_RESULT_DENIED`.
- `AXIOM-06.2` **MUST** declare version ranges, never exact versions, for `axiom`, game API modules and dependencies; the upper bound is the next major.
- `AXIOM-06.3` **MUST** declare load order with `after` / `before` and `phase`, never by relying on file names or install order.
- `AXIOM-06.4` **MUST** bump `version` for every published change, following semantic versioning (§21).
- `AXIOM-06.5` **MUST NOT** put secrets, tokens or machine-specific paths in the manifest.
- `AXIOM-06.6` **SHOULD** keep `description` under 120 characters; it is shown in the launcher and registry.

## 07. Plugin entry point and lifecycle

- `AXIOM-07.1` **MUST** keep every callback idempotent and safe to call in order load → enable → disable → enable → disable → unload. Players toggle plugins at runtime.
- `AXIOM-07.2` **MUST** leave the game exactly as it was found after `on_disable`: every hook, event subscription, spawned entity and UI element the plugin added is gone.
- `AXIOM-07.3` **SHOULD** register subscriptions and hooks through scoped handles the SDK releases automatically on disable, rather than tracking them by hand.
- `AXIOM-07.4` **MUST** return from each callback within the lifecycle budget (§18); slow initialisation goes into an async task.
- `AXIOM-07.5` **MAY** implement `on_save_state` / `on_restore_state` to keep state across hot reloads during development.

## 08. Calling the C ABI correctly

- `AXIOM-08.1` **MUST** check the `ax_result_t` of every call that returns one. `AX_RESULT_DENIED` means a capability is missing; `AX_RESULT_NOT_READY` means the game is not in a state where the call is valid.
- `AXIOM-08.2` **MUST** set `struct_size = sizeof(...)` on every struct passed to Axiom. This is how newer cores stay compatible with your plugin; a zero `struct_size` is rejected.
- `AXIOM-08.3` **MUST** zero-initialise structs before filling them (`ax_event_desc_t d = {0};`) so fields added in later minor versions start at their defaults.
- `AXIOM-08.4` **MUST** pass strings as `ax_str_t` (UTF-8 pointer + length; use `AX_STR("literal")`), never assume Axiom returns null-terminated strings.
- `AXIOM-08.5` **MUST** use only fixed-width types at the boundary (`int32_t`, `uint64_t`, `float`, `ax_bool_t`); never `bool`, `long`, `wchar_t` or a C++ type.
- `AXIOM-08.6` **MUST** treat every handle (`ax_entity_t`, `ax_subscription_t`) as opaque and possibly stale: check results when using a handle you stored across frames.
- `AXIOM-08.7` **MUST** mark callbacks `AX_CALL` and keep them non-throwing (C++: `noexcept`), and pass a `user_data` pointer instead of using globals.
- `AXIOM-08.8` **MUST** check the API version you were given at the entry point and call newer functions only when `api->version_minor` says they exist (the SDK's `ax_has(api, fn)` macro does this).
- `AXIOM-08.9` **MUST NOT** call Axiom from threads Axiom did not give you unless the function is documented as thread-safe (§10).

## 09. Events, hooks and talking to other plugins

- `AXIOM-09.1` **SHOULD** react to events instead of polling game state every frame.
- `AXIOM-09.2` **MUST** keep event handlers short and non-blocking; heavy work is scheduled as a task (§10).
- `AXIOM-09.3` **MUST** treat event payloads as borrowed: valid only during the callback. Copy what you need.
- `AXIOM-09.4` **MUST** use priorities only to express a real ordering need, and document it. Lower numbers run first; the default is `0`.
- `AXIOM-09.5` **MUST** cancel a cancellable event (`ax_event_cancel`) only when the plugin's purpose is to block that action, and log why at `debug` level.
- `AXIOM-09.6` **MUST** use the weakest hook kind that works: `observe` over `post`, `post` over `pre`.
- `AXIOM-09.7` **MUST** skip the original in a `pre` hook only when the plugin replaces that behaviour completely; two plugins skipping the same original is a conflict the core reports.
- `AXIOM-09.8` **MUST NOT** keep a hook's argument pointers after the hook returns.
- `AXIOM-09.9` **MUST** talk to other plugins through **services** (a versioned interface a plugin registers under its ID) or plugin-defined events, never by reaching into another plugin's memory or files.
- `AXIOM-09.10` **MUST** version every service interface with semantic versioning, and declare the dependency in the manifest.
- `AXIOM-09.11` **MUST** handle an optional dependency being absent or disabled at any time; check `ax_service_get` every time you need it, or listen for `plugin.disabled`.
- `AXIOM-09.12` **SHOULD** pass plain data (structs, strings, byte buffers) through services, not handles that only make sense inside one language provider.

## 10. Threading and the game loop

- `AXIOM-10.1` **MUST** touch game state and call game API functions only from the game thread, inside a callback or hook, unless the function is documented as thread-safe.
- `AXIOM-10.2` **MUST NOT** block the game thread: no sleeping, no synchronous network or disk I/O, no waiting on locks held by worker threads.
- `AXIOM-10.3` **MUST** move slow work to the task pool with `ax_task_spawn` and bring results back with `ax_task_post_to_game` (or the SDK's `ctx.spawn(...).then_on_game(...)`).
- `AXIOM-10.4` **MUST NOT** create raw OS threads in a plugin. Native plugins that need a dedicated thread request one through `ax_thread_create` so the core can name, track and stop it on disable.
- `AXIOM-10.5` **MUST** cancel and wait for every task a plugin started before `on_disable` returns; the SDK's scoped tasks do this.
- `AXIOM-10.6` **MUST** keep state shared between a task and the game thread behind the SDK's channels or a mutex, never as unsynchronised globals.

## 11. Memory and ownership across the boundary

- `AXIOM-11.1` **MUST** free memory with the allocator that allocated it. Memory from `ax_alloc` goes back through `ax_free`; never mix with `free`, `delete` or another language's allocator.
- `AXIOM-11.2` **MUST NOT** pass ownership of a C++ object, a `std::` type, a Rust `Box` or a Lua table across the ABI; pass plain data or a handle.
- `AXIOM-11.3` **MUST** release every handle and buffer a plugin owns by `on_unload`. The core reports leaks per plugin in development builds.
- `AXIOM-11.4` **MUST** treat handles to game objects (`ax_entity_t`) as weak: the object can be destroyed between frames. Re-validate with `ax_<game>_entity_is_valid` or listen for the destroy event.
- `AXIOM-11.5` **SHOULD** use the SDK's RAII or scoped wrappers (`ax::Handle<T>` in C++, `Drop` types in Rust) instead of manual release calls.
- `AXIOM-11.6` **MUST** in providers: validate every pointer and length that comes from a plugin before reading it, and copy guest data out of WASM linear memory before using it.

## 12. Errors and crash safety

- `AXIOM-12.1` **MUST** handle every error result. Expected failures (missing config, denied capability, entity gone) are logged at `warn` or `debug` and the plugin carries on.
- `AXIOM-12.2` **MUST** return a failure from `on_load` or `on_enable` when the plugin cannot work, with a message that tells the player what to do ("requires acme.ui-kit 0.9 or newer").
- `AXIOM-12.3` **MUST NOT** let an exception, Rust panic or Lua error escape a callback. SDKs catch them at the boundary, log them with a stack trace and disable the plugin; native plugins [MUST] do the same (`noexcept` callbacks with a `try` / `catch` at the edge).
- `AXIOM-12.4` **MUST NOT** call `abort`, `exit`, `std::terminate` or anything that ends the process.
- `AXIOM-12.5` **MUST** isolate every call into a plugin: catch C++ exceptions, and on Windows use structured exception handling around native plugin calls, so a crash in a plugin disables that plugin instead of the game.
- `AXIOM-12.6` **MUST** disable a plugin that exceeds its limits (instruction budget, memory quota, frame-time budget repeated across frames) and tell the player which plugin and why.
- `AXIOM-12.7` **MUST** write a crash report on a real crash (minidump on Windows) that names the plugins and providers loaded and which one was running, and [MUST NOT] upload it without the player's consent.
- `AXIOM-12.8` **MUST** fail closed when a game build is unknown: refuse to install hooks and report "unsupported game version" rather than patching memory that may have moved.
- `AXIOM-12.9` **MUST** write player-facing errors as: what happened, which plugin, what to do. "Better Minimap was disabled: it needs the `ui:draw` permission. Allow it in the launcher."
- `AXIOM-12.10` **SHOULD** give every error a stable code (`BMM-0012`) so players can search for it and you can find it in logs.

## 13. Writing a game provider

- `AXIOM-13.1` **MUST NOT** do work in `DllMain` or a shared-library constructor beyond recording the module handle: no allocation, threads, locks, logging or library loads under the loader lock.
- `AXIOM-13.2` **MUST NOT** rely on static initialisation order or non-trivial global constructors; create and destroy state in `attach` / `detach`.
- `AXIOM-13.3` **MUST** link the C runtime statically on Windows (`/MT`) so the provider never conflicts with the game's CRT.
- `AXIOM-13.4` **MUST NOT** change process-wide state the game depends on (FPU control word, locale, working directory, signal or exception handlers, COM apartment) without restoring it on the same thread.
- `AXIOM-13.5` **MUST** be able to stop and detach cleanly at runtime: every hook removed, every thread joined, every allocation returned.
- `AXIOM-13.6` **MUST** use `std::expected<T, Axiom::Error>` for recoverable failures and keep every function the core or a hook can call `noexcept`.
- `AXIOM-13.7` **SHOULD** hook managed games (.NET, JVM) at the runtime level through the runtime's supported profiling or agent interfaces before resorting to native patches.
- `AXIOM-13.8` **MUST** in emulated targets, hook the emulator's guest-facing layer (CPU, memory, HLE calls), not the emulator's UI or host code, and translate guest addresses through the emulator's memory map every time.

## 14. Game providers: hooking, signatures and game updates

- `AXIOM-14.1` **MUST** install every hook through Axiom's hook manager (`Axiom::Hooks`), never with a private detour library, so hooks from different providers are ordered, tracked and removable.
- `AXIOM-14.2` **MUST** suspend or synchronise game threads while patching code, and verify the bytes being replaced match what the signature expected.
- `AXIOM-14.3` **MUST** keep each native hook as thin as possible: capture arguments, call into the core's dispatch, return. Game logic lives in the game API module, not in the trampoline.
- `AXIOM-14.4` **MUST** preserve the hooked function's calling convention, registers and stack alignment exactly; write a test for each hook that calls the original through it.
- `AXIOM-14.5` **SHOULD** hook the narrowest stable function that gives plugins what they need, and prefer one hook that feeds many events over many hooks.
- `AXIOM-14.6` **MUST** locate game code and data by byte-pattern signatures, exported symbols, RTTI or metadata — never by hard-coded absolute addresses.
- `AXIOM-14.7` **MUST** keep signatures and offsets in data files per game build (`Signatures/<build>.toml`), each with a name, pattern, expected match count and the build it was verified on.
- `AXIOM-14.8` **MUST** fail closed: if any required signature has zero or more than one match, refuse to start and report which one.
- `AXIOM-14.9` **SHOULD** resolve all signatures once in `attach`, in parallel, and cache the result per build hash.
- `AXIOM-14.10` **MUST** detect the game build exactly (file hash or version resource) and list supported builds in the provider manifest.
- `AXIOM-14.11` **MUST** treat a new game build as unsupported until the signatures are verified on it; the launcher tells players to wait for a provider update.
- `AXIOM-14.12` **SHOULD** run the provider conformance suite against every supported build in CI, and keep at least the last two builds of a live-service game supported while players update.

## 15. Game API modules: exposing a game to plugins

- `AXIOM-15.1` **MUST** define the module in `spec/` (IDL). `AxiomGen` generates the C header (`axiom/mygame.h`), the size asserts, every SDK binding and the reference pages from it. Generated files [MUST NOT] be edited by hand.
- `AXIOM-15.2` **MUST** land a spec change, its generated output and an example plugin that uses it in the same pull request.
- `AXIOM-15.3` **MUST** expose game concepts, not game internals: `ax_mygame_player_get_health(player)` rather than a pointer to the game's `Player` struct or an offset.
- `AXIOM-15.4` **MUST** use stable identifiers that survive game updates (names, hashed names, provider-assigned IDs), never raw addresses.
- `AXIOM-15.5` **MUST** separate queries (no side effects) from commands (change state), and state which phases each may be called in.
- `AXIOM-15.6` **SHOULD** publish events for state changes plugins would otherwise poll (`player.health.changed`), and hook points for behaviour they may change (`mygame.damage.apply`).
- `AXIOM-15.7` **SHOULD** offer batched calls for things plugins do in loops (`ax_mygame_entities_query` filling a caller buffer), because each boundary crossing from WASM or Lua has a fixed cost.
- `AXIOM-15.8` **MUST** gate writes and anything that affects multiplayer or other players behind capabilities (`game:write`, `game:network`).
- `AXIOM-15.9` **MUST** keep the module the same across supported game builds; differences between builds are the provider's problem, hidden behind the module.
- `AXIOM-15.10` **MUST** version the game API module independently of the provider binary and of Axiom; a game update that only needs new signatures does not change the module version.
- `AXIOM-15.11` **MUST** deprecate before removing: mark it in the spec, warn once per plugin at load, and keep it working until the next major.

## 16. Writing a language provider

- `AXIOM-16.1` **MUST** expose exactly the API the C ABI defines, generated by AxiomGen from the same spec. A function available to native plugins but missing in your language is a bug.
- `AXIOM-16.2` **MUST** give every plugin its own isolated instance (a WASM instance, a Lua state); plugins never share globals.
- `AXIOM-16.3` **MUST** enforce quotas: memory caps, and fuel, epoch or instruction-count interruption so an infinite loop is stopped and the plugin disabled, not the game frozen.
- `AXIOM-16.4` **MUST** remove ambient authority from the language: no WASI file, clock, random or network imports and no Lua `os`, `io`, `debug`, `package.loadlib` or FFI, except as granted by capabilities.
- `AXIOM-16.5` **MUST** bounds-check every (pointer, length) pair a guest passes, and copy data out of guest memory before using it.
- `AXIOM-16.6` **MUST** convert language-level failures (WASM traps, Lua errors, panics) into an error result plus a log entry with the plugin's own stack trace.
- `AXIOM-16.7` **MUST** ship an SDK for the language with idiomatic wrappers: scoped handles, typed events, the language's normal error type and async model.
- `AXIOM-16.8` **SHOULD** support hot reload in development builds: save state, unload, load the new code, restore.
- `AXIOM-16.9` **MUST** embed the engine (WASM runtime, Lua VM) behind your provider's own interface and pin its version, so it can be upgraded without changing plugins.
- `AXIOM-16.10` **MUST** host the .NET runtime once per game process (through `hostfxr`), pinned to one supported .NET LTS version that the provider ships with; plugins never bring their own runtime.
- `AXIOM-16.11` **MUST** load each plugin into its own collectible `AssemblyLoadContext` so it can be unloaded and hot-reloaded, and [MUST] release every GC handle and delegate pointing into a plugin before unloading it, or the context will never collect.
- `AXIOM-16.12` **MUST** marshal across the C ABI with `[UnmanagedCallersOnly]` entry points and blittable structs generated by AxiomGen; no `Marshal.GetDelegateForFunctionPointer` or COM interop at the boundary.
- `AXIOM-16.13` **MUST** catch every exception at the boundary and never let one unwind into native frames.
- `AXIOM-16.14` **MUST** treat hosted .NET plugins as trusted (tier 3): signed packages only, `process:native` capability required.
- `AXIOM-16.15` **SHOULD** run plugin callbacks on the game thread without allocating, keeping per-frame paths free of LINQ, closures and boxing so the .NET GC does not cause frame spikes.
- `AXIOM-16.16` **MUST** target the same API surface for both C# paths: one `Axiom` NuGet package whose code builds for the WASI target and for hosted .NET.
- `AXIOM-16.17` **MUST** follow the standard .NET conventions in C# plugins: PascalCase public members, `async` / `await` for tasks (mapped onto Axiom's task pool), nullable reference types enabled, warnings as errors.
- `AXIOM-16.18` **SHOULD** expose events as C# events or `IObservable<T>`, and scoped resources as `IDisposable`, so `using` releases them automatically.

## 17. Capabilities and security

- `AXIOM-17.1` **MUST** request the fewest capabilities that work, and explain every optional one in the manifest.
- `AXIOM-17.2` **MUST** handle `AX_RESULT_DENIED` gracefully: turn the feature off and tell the player, never retry in a loop.
- `AXIOM-17.3` **MUST** list the exact hosts a plugin connects to (`[network] hosts = ["api.acme.example"]`) and use TLS.
- `AXIOM-17.4` **MUST NOT** collect personal data, telemetry or crash reports without the player's opt-in, and [MUST] say what is sent in the README.
- `AXIOM-17.5` **MUST NOT** include obfuscated code, downloaders that fetch new code at runtime, or anything that bypasses a game's anti-cheat or multiplayer rules.
- `AXIOM-17.6` **MUST** enforce capabilities in the provider for every call that needs one; never trust the plugin's SDK to have checked.
- `AXIOM-17.7` **MUST** load native (tier 3) plugins only when signed by a trusted key or explicitly allowed by the player in developer mode.
- `AXIOM-17.8` **MUST** verify package signatures and hashes before loading any plugin or provider.
- `AXIOM-17.9` **MUST** refuse to run plugins with `game:write` or `game:network` in online modes the game provider marks as protected.

## 18. Performance budgets

- `AXIOM-18.1` **MUST** stay within budget on the reference machine the game's minimum specs describe.
- `AXIOM-18.2` **MUST** measure before optimising, with `axiom profile` or the in-game overlay, and include before and after numbers in performance pull requests.
- `AXIOM-18.3` **SHOULD** cache results that do not change every frame, and spread periodic work across frames instead of doing it all on one.
- `AXIOM-18.4` **SHOULD** use batched game API calls in WASM and Lua plugins; each boundary crossing has a fixed cost.
- `AXIOM-18.5` **MUST NOT** allocate on hot paths in native plugins and providers (per-frame handlers, hook trampolines); reuse buffers.
- `AXIOM-18.6` **MUST** in providers: make a hook point cost nothing when no plugin subscribes to it, by checking a subscriber count before dispatching.

## 19. Logging and diagnostics

- `AXIOM-19.1` **MUST** log through Axiom's logger (`ax_log`, or the SDK's `log` macros); never `printf`, `std::cout`, `print` or the game's own console. Axiom tags each line with the plugin or provider ID.
- `AXIOM-19.2` **MUST** use levels consistently: `trace` and `debug` for development detail, `info` for lifecycle and rare player-relevant events, `warn` for handled problems, `error` for failures that disabled a feature.
- `AXIOM-19.3` **MUST NOT** log every frame at `info` or above. Rate-limit repeated messages (`ax_log_once`, `log::warn_every!(5s, ...)`).
- `AXIOM-19.4` **MUST NOT** log secrets, tokens, or personal data such as player names or chat in non-debug levels.
- `AXIOM-19.5` **SHOULD** add structured fields instead of formatting them into text: `log::info!(zoom = self.zoom; "minimap enabled")`.
- `AXIOM-19.6` **SHOULD** expose a debug panel in the in-game overlay (`ax_debug_panel_add`) for plugin state, instead of verbose logging.
- `AXIOM-19.7` **MUST** in providers: report per-plugin frame time, memory and hook counts to Axiom's diagnostics so the overlay and `axiom profile` can show them, and include the provider version and game build in every crash report.

## 20. Testing plugins and providers

- `AXIOM-20.1` **MUST** unit-test plugin logic with the language's normal test runner, tests beside the code (`minimap.test.rs`, `minimap.test.lua`).
- `AXIOM-20.2` **MUST** test behaviour against the **mock host** (`axiom test`): a fake core and fake game API module that let tests fire events, call hooks, advance frames and assert on what the plugin did.
- `AXIOM-20.3` **MUST** test the full lifecycle at least once: load → enable → disable → enable → disable → unload, asserting nothing leaks and nothing stays hooked.
- `AXIOM-20.4` **MUST** test the denied-capability and missing-optional-dependency paths.
- `AXIOM-20.5` **SHOULD** run an in-game smoke test (`axiom run --game mygame --headless --script smoke.toml`) before each release when the game supports it.
- `AXIOM-20.6` **MUST** pass the **provider conformance suite**: lifecycle, attach and detach, hook install and removal, event delivery, capability checks and crash isolation.
- `AXIOM-20.7` **MUST** verify every signature file against its game build in CI (match counts and expected bytes) and fail on any drift.
- `AXIOM-20.8` **MUST** test each hook by calling the original through it with known arguments and comparing the result to an unhooked call.
- `AXIOM-20.9` **SHOULD** keep a set of real community plugins as a compatibility corpus and run it against every provider release.
- `AXIOM-20.10` **MUST** run the shared API conformance tests through your language's SDK, proving every ABI function is reachable and behaves the same as from C.
- `AXIOM-20.11` **MUST** test quota enforcement: an infinite loop, a memory bomb and a deep recursion are each stopped and the plugin disabled, with the game still running.
- `AXIOM-20.12` **MUST** fuzz the marshalling layer with malformed guest pointers and lengths.
- `AXIOM-20.13` **MUST** build and test on every supported platform, and run providers' tests under ASan and UBSan builds as well as the release build.

## 21. Packaging, versioning and publishing

- `AXIOM-21.1` **MUST** follow semantic versioning for plugins, providers, game API modules and services.
- `AXIOM-21.2` **MUST** migrate a plugin's saved data and config forward automatically on upgrade, and keep a schema version in every saved file.
- `AXIOM-21.3` **MUST** keep a `CHANGELOG.md` generated from Conventional Commits, with player-facing notes at the top of each release.
- `AXIOM-21.4` **MUST** build packages with `axiom pack` only. A `.axmod` contains the manifest, the compiled plugin (`.wasm`, Lua sources, or one native library per platform), assets, licences and a file hash list.
- `AXIOM-21.5` **MUST** sign every published package with the author's registry key; native plugins and all providers [MUST] be signed.
- `AXIOM-21.6` **MUST** make builds reproducible: pinned toolchains, lock files, no timestamps or absolute paths in the output.
- `AXIOM-21.7` **MUST NOT** include debug symbols, source maps or test data in the published package; publish symbols separately for crash reports.
- `AXIOM-21.8` **MUST** fill in the registry listing: description, screenshots, supported games and builds, capabilities with reasons, source link and licence.
- `AXIOM-21.9` **MUST** publish open-source plugins with an OSI licence file at the root; closed-source plugins are allowed but cannot request `process:native`.
- `AXIOM-21.10` **SHOULD** release to a `beta` channel first and promote to `stable` after it has run without new crash reports.

## 22. Git, releases and review

- `AXIOM-22.1` **MUST** follow **Conventional Commits**: `type(scope): summary`, imperative, lower case, no trailing period, under 72 characters. Scope is the subsystem: `feat(waypoints): add party sync`, `fix(signatures): update for 1.18.4`.
- `AXIOM-22.2` **MUST** mark breaking changes with `!` and a `BREAKING CHANGE:` footer; these require a major version bump.
- `AXIOM-22.3` **MUST** keep commits small and focused, each building and passing tests.
- `AXIOM-22.4` **MUST** branch from `main` with a typed name (`feat/party-sync`, `fix/1.18.4-signatures`), and tag releases `v<semver>`.
