style
esc
  • Loading…

For agentsEnma Style Guide

Conventions for writing Enma scripts, embedding the Enma SDK, and building single-player scripts on Perception.

The same guide as plain Markdown, built from the same source as the web edition. Rules stay machine-readable: each one starts with its stable ID and level, such as `ENMA-02.1` [MUST], so an agent can cite exactly the rule it means.

Rules digest

~8.4k tokens

191 rules, grouped by chapter, no prose or examples. Small enough to keep in an agent's context on every task.

Full guide

~12.9k tokens

Every chapter, table, code sample and drop-in config as one Markdown file. Best when the agent can read files on demand.

Pin a revision

The URLs above always serve the latest text, so an edit to the guide changes what your agent follows. Add @2026.10 after the guide name for a copy that never changes; the snippets below already do. To upgrade, read the changelog for the rules that were added, removed or changed, then bump the revision.

# Revision 2026.10: these files never change
curl -fsSL https://style.chopper.dev/enma@2026.10/rules.md
curl -fsSL https://style.chopper.dev/enma@2026.10/llms.txt

# What changed between revisions
curl -fsSL https://style.chopper.dev/enma/changelog.md

Template project

A Windows Premake host that embeds the Enma SDK through RAII engine, module and context owners, a script package laid out per the guide with tests beside each script, budget, permission and .emb round-trip tests, the clang-format config, and a CI workflow. Both Markdown files above describe it, so an agent asked to start a new Enma codebase can fetch a renamed copy instead of scaffolding one by hand.

curl -fsSL "https://style.chopper.dev/enma/template/download?name=Project&code=PRJ" \
  -o Project.zip && unzip Project.zip
ParameterDefaultMeaning
nameProjectPascalCase. Names the host app, its namespace and the script package.
codePRJ2 or 3 letters. Prefixes the host's macros.

Use it with your tools

MCP server / Claude Code plugin

Connect the remote MCP server, or install the plugin, which adds the server plus a enma-style skill that loads these rules whenever Claude writes or reviews Enma.

claude mcp add --transport http chopper-style https://style.chopper.dev/mcp

# or the plugin: MCP server + one skill per guide
claude plugin marketplace add carson-hopper/chopper.dev
claude plugin install chopper-style@chopper-dev

Claude Code / AGENTS.md

Save the guide into the repo, then point the project instructions file (CLAUDE.md or AGENTS.md) at it.

curl -fsSL https://style.chopper.dev/enma@2026.10/llms.txt -o docs/ENMA_STYLE.md

# CLAUDE.md / AGENTS.md
## Enma style
All Enma code follows docs/ENMA_STYLE.md (rev. 2026.10). Rules marked
[MUST] / [MUST NOT] are non-negotiable; [SHOULD] rules need a comment when broken.
Cite rules by ID (ENMA-02.1) in reviews and comments.

Cursor

A project rule that applies the digest whenever a Enma file is in play.

mkdir -p .cursor/rules
{ printf -- '---\ndescription: Enma Style Guide\nglobs: "**/*.{em,h,hpp,cpp}"\nalwaysApply: false\n---\n\n'
  curl -fsSL https://style.chopper.dev/enma@2026.10/rules.md; } > .cursor/rules/enma-style.mdc

Any chat assistant

Paste this ahead of the request, or attach the digest file.

Follow the Enma Style Guide at https://style.chopper.dev/enma@2026.10/rules.md for all Enma code you write or review. Treat MUST / MUST NOT as hard constraints and cite the rule's ID (such as ENMA-02.1) when you flag a violation.

Digest preview

# Enma Style Guide: rules digest

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

Revision 2026.10, plus edits not yet released. This URL always serves the latest text; pin a revision with https://style.chopper.dev/enma@2026.10/rules.md. Changelog: https://style.chopper.dev/enma/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 (ENMA-02.1): IDs never change or get reused, and https://style.chopper.dev/enma#ENMA-02.1 links to the rule. Examples use a fictional host, `Project`, whose prefix is `PRJ`. Enma is deliberately C++-faithful, so the C++ Style Guide (https://style.chopper.dev/cpp) applies to Enma scripts wherever the language allows it, and in full to host code.

## Starter project

A Windows Premake host that embeds the Enma SDK through RAII engine, module and context owners, a script package laid out per the guide with tests beside each script, budget, permission and .emb round-trip tests, the clang-format config, and a CI workflow. It is generated from this guide and follows it, so prefer it over scaffolding a new codebase by hand.

Download it as a zip, renamed to the user's names, with a GET request (query parameters are optional; missing ones take their defaults, invalid ones return HTTP 400 with a message):

```bash
curl -fsSL "https://style.chopper.dev/enma/template/download?name=Project&code=PRJ" -o Project.zip && unzip Project.zip
```

Parameters:

- `name`: Project name. PascalCase. Names the host app, its namespace and the script package. Must match `^(?:[A-Z][A-Za-z0-9]{1,31})$`. Default `Project`.
- `code`: Host prefix. 2 or 3 letters. Prefixes the host's macros. Must match `^(?:[A-Z]{2,3})$`. Default `PRJ`.

After unzipping:

1. **Vendor the SDK.** Copy the Enma SDK's shipped/ folder into vendor/enma/, then run tools\Setup.bat to add doctest and generate the solution.
2. **Build and test.** Build the solution in Visual Studio, then run the -Tests executable and the app from the repository root.

## 02. Baseline and toolchain

- `ENMA-02.1` **MUST** link the SDK library that matches the host's CRT setting. Mixing `/MT` and `/MD` fails at link time; pick one for the whole host.
- `ENMA-02.2` **MUST** give every thread that calls `compile()` (or `compile_file`, `link`, `reload`) at least **2 MB** of stack. Raise the main thread's reserve with the linker's `/STACK` option and create compile threads with an explicit stack size.
- `ENMA-02.3` **MUST** compile only the addon source files the host registers; unused addons stay out of the build.
- `ENMA-02.4` **MUST** keep the SDK version pinned (vendored or by exact release) and upgrade it deliberately, reading the changelog, since the language tracks the newest C++ semantics.
- `ENMA-02.5` **SHOULD** turn on the language server in every editor used for `.em` files, and treat its diagnostics as build errors.
- `ENMA-02.6` **SHOULD** keep `set_optimize(e, true)` (the default) in every build; use `set_debug(e, true)` only in development hosts.

## 03. Project layout

- `ENMA-03.1` **MUST** keep Enma sources under one `scripts/` root and register it with `add_module_path`, so imports resolve the same way in every build.
- `ENMA-03.2` **MUST** keep tests beside the code they test: `<name>.test.em` for scripts and `<File>.test.cpp` for host code.
- `ENMA-03.3` **MUST NOT** commit `.emb` artifacts; build them in CI from `.em` sources and ship them as release assets.
- `ENMA-03.4` **MUST** keep every native registration in the host's `Scripting/Natives/` folder, one file per area, so the full script-visible surface can be reviewed in one place.
- `ENMA-03.5` **SHOULD** keep one module per file and name the file after the namespace it provides (`MathUtils.em` → `MathUtils::`).
- `ENMA-03.6` **SHOULD** keep `Main.em` (or the host's named entry functions) thin: it wires modules together and holds no game or business logic.

## 04. Naming

- `ENMA-04.1` **MUST NOT** name anything with a leading double underscore; `__` is reserved for Enma intrinsics (`__asm_rdtsc`, `__reflect_count`).
- `ENMA-04.2` **MUST NOT** shadow standard-surface names or builtins (`println`, `assert`, `time_ms`, `set_budget`).
- `ENMA-04.3` **MUST** start every native's name with its area (`Input`, `Audio`, `World`) and register each area from one host file, so a reader knows the call crosses into the host.
- `ENMA-04.4` **MUST** keep the module file name and its namespace identical; an `import` alias (`import "Lib/MathUtils.em" as Mu;`) is only for resolving a clash.

## 05. Formatting and linting

- `ENMA-05.1` **MUST** format `.em` files with `clang-format` using the same `.clang-format` as the host (2-space indent, 100 columns, attached braces); Enma's syntax is close enough to C++ that the formatter handles it. Exclude files that use `match` expressions if the formatter mangles them, and format those by hand to the same style.
- `ENMA-05.2` **MUST** treat every compiler and language-server diagnostic as an error. A script that compiles with warnings does not merge.
- `ENMA-05.3` **MUST** run a compile of every `.em` file in CI, through the same host configuration (addons, permissions, defines) that ships.
- `ENMA-05.4` **MUST** run the review checklist from Enma's own guidance on every change (Appendix C): no `short` / `long` / `unsigned`, no standard-header `#include`, no `->` on values, only `std::` names the STD Library page lists, every `new` paired with a `delete`.
- `ENMA-05.5` **SHOULD** brace every `if`, `for` and `while` body, even one-liners.
- `ENMA-05.6` **SHOULD** keep one declaration per line; Enma allows `int64 x = 1, y, z = 3;` but it hides the uninitialised `y`.
- `ENMA-05.7` **SHOULD** order a module: imports, `#include`s, `using` aliases, constants, types, then functions, with `main` last in an entry module.

## 06. Types and values

- `ENMA-06.1` **MUST** use Enma's sized types: `int8`–`int64`, `uint8`–`uint64`, `float32`, `float64`, `bool`, `char`, `wchar`. [MUST NOT] write `short`, `long`, `unsigned` or `signed`; they are not type names.
- `ENMA-06.2` **SHOULD** use `int64` for counts, sizes, IDs and anything returned to the host; use narrower types only for storage that is packed or sent somewhere.
- `ENMA-06.3` **AVOID** the aliases `int`, `float` and `double` in new code. They are legal (`int32`, `float32`, `float64`), but writing the width removes any doubt about what size a value is. Use the `<cstdint>` spellings (`int32_t`, `size_t`) only in code shared with C++.
- `ENMA-06.4` **MUST** remember unsuffixed literals: integers are `int32`, `1.5` is `float64`, `1.5f` is `float32`. Suffix wide constants (`18446744073709551615ull`).
- `ENMA-06.5` **MUST** make every narrowing or sign-changing conversion explicit with `static_cast`, even where Enma would accept it silently. The cast is the reviewer's signal that the wrap is intended.
- `ENMA-06.6` **MUST** use brace initialisation for new variables (`int32 x{42};`). Braces reject narrowing (`uint8 u{-1};` is a compile error), which catches the silent wraps above.
- `ENMA-06.7` **MUST** use `enum class` for new enums; convert to integers only with `static_cast`.
- `ENMA-06.8` **MUST** use `std::string` / `std::wstring` for owned text; there is no bare `string`. `const char*` is only for literals and native signatures.
- `ENMA-06.9` **MUST** build output text before printing: `println` takes one `std::string` or `const char*`, so use `std::format` or `std::to_string`.
- `ENMA-06.10` **SHOULD** use raw strings for paths and patterns: `R"(C:\dir\file)"`.
- `ENMA-06.11` **SHOULD** return `std::optional<T>` for "maybe a value" instead of sentinel values like `-1`.
- `ENMA-06.12` **MUST** use `aint8`–`aint64` for integer globals touched by more than one context (§15); compound operations on them are atomic.

## 07. Functions, lambdas and control flow

- `ENMA-07.1` **MUST** declare the entry point as `int64 main()` (or `int32 main()`): no parameters, not `static`.
- `ENMA-07.2` **SHOULD** keep functions short (about 40 lines) with at most four parameters; group more into a struct.
- `ENMA-07.3` **MUST** pass large structs and containers by `const&`; Enma structs are value types and copy on pass-by-value.
- `ENMA-07.4` **MUST** keep deduced (`auto`) return types consistent across every `return`. When a function mixes widths, declare the return type instead of relying on deduction.
- `ENMA-07.5` **MUST NOT** declare overloads that differ only in return type.
- `ENMA-07.6` **MUST NOT** rely on argument evaluation order even though Enma fixes it left to right (unlike C++); a call like `Pair(Next(), Next())` reads as a bug to anyone who knows C++. Evaluate into named locals first.
- `ENMA-07.7` **SHOULD** use default parameters only for genuinely optional settings, and never refer to a later parameter.
- `ENMA-07.8` **MUST** use `auto` or `delegate` to hold capturing lambdas; only non-capturing lambdas convert to function pointers (`+[]{ ... }`).
- `ENMA-07.9` **MUST NOT** return a lambda that captures a local by reference; the compiler rejects it, and the by-value case with a pointer to a local is not caught. Capture values, or own the data with `new`.
- `ENMA-07.10` **SHOULD** keep lambdas under about ten lines; name anything longer.
- `ENMA-07.11` **SHOULD** use `match` for value-to-value mappings, always with a `_` arm:
- `ENMA-07.12` **SHOULD** use range `for` with `const&` (`for (const auto& [name, score] : scores)`) instead of index loops when the index is not needed.
- `ENMA-07.13` **SHOULD** use `defer { ... }` right after acquiring a resource that has no destructor of its own; it runs at scope exit and during unwinding.
- `ENMA-07.14` **MUST NOT** use `goto` except to break out of nested loops, and never into a `try`, handler or `defer` body (the compiler refuses it).
- `ENMA-07.15` **MUST** return early instead of nesting; no `else` after `return`.

## 08. Structs, classes and templates

- `ENMA-08.1` **MUST** use `struct` for plain data with public PascalCase fields and no invariants, and `class` for types with private state (`m_` members) and methods that keep invariants. They differ only in default access, so the keyword is how you signal intent.
- `ENMA-08.2` **MUST** declare objects on the stack as `T x;` or `T x{...};`, and on the heap as `T* x = new T(...);`. `T x = new T();` is rejected; `T x();` declares a function.
- `ENMA-08.3` **MUST** use `.` on values and references and `->` only on pointers or types that declare `operator->()`; the wrong one is a compile error.
- `ENMA-08.4` **MUST** give every member a default initialiser or set it in the constructor's initialiser list; aggregates with excess initialisers are rejected.
- `ENMA-08.5` **SHOULD** keep inheritance single and shallow. Use `virtual` only when you need runtime dispatch, mark overrides with `override`, and give polymorphic bases a `virtual` destructor.
- `ENMA-08.6` **AVOID** multiple and virtual inheritance; Enma supports them, but a non-virtual diamond silently creates two base subobjects.
- `ENMA-08.7` **MUST** convert class types at the call site; Enma does not apply class-to-class user conversions to function arguments.
- `ENMA-08.8` **MUST NOT** make const data members in classes that need copy assignment; Enma removes copy assignment from such classes.
- `ENMA-08.9` **SHOULD** use templates for genuinely generic code (containers, math over numeric types) and constrain them with concepts.
- `ENMA-08.10` **MUST** write `typename` before a dependent type name inside a template (`typename T::Type`).
- `ENMA-08.11` **MUST** remember that two instantiations of one template are different types: a value crossing a parameter, return or `new` boundary must match exactly.
- `ENMA-08.12` **AVOID** deep template metaprogramming; Enma caps instantiations at 16,384 per compile and nesting at 1,024, and every instantiation is monomorphised into machine code.
- `ENMA-08.13` **SHOULD** prefer `constexpr` functions and `if constexpr` over recursive template tricks for compile-time work.

## 09. Memory and ownership

- `ENMA-09.1` **MUST** prefer stack values and `std` containers. Reach for `new` only when an object must outlive its scope or be shared by address.
- `ENMA-09.2` **MUST** pair every `new` with exactly one `delete` and every `new T[N]` with `delete[]`, and make the owner obvious: one struct or function owns each heap object and deletes it in its destructor or with `defer`.
- `ENMA-09.3` **SHOULD** put `defer { delete p; }` on the line after a `new` whose object never leaves the function.
- `ENMA-09.4` **MUST NOT** return, store in a global or field, or capture by reference the address of a local. The compiler rejects the obvious cases; its escape analysis is conservative, so do not lean on it for the rest.
- `ENMA-09.5` **SHOULD** mark functions that must never leak allocations with `[[noescape]]`; the compiler then rejects any allocation that outlives the call.
- `ENMA-09.6` **MUST** index with `.at(i)` when the index comes from input or computation that could be wrong. `v[i]`, `a[i]` and `p[i]` are **not** bounds-checked; a near miss is undefined behaviour.
- `ENMA-09.7` **MUST** treat a pointer from a native as borrowed unless the native is documented as transferring ownership, and never `delete` it.
- `ENMA-09.8` **AVOID** mutable globals. They are shared by every context of a module (§15); keep state in structs passed by reference, or in context userdata set by the host.
- `ENMA-09.9` **SHOULD** check `heap_count()` in tests before and after a scenario to catch leaks.

## 10. Errors and exceptions

- `ENMA-10.1` **MUST** throw exceptions for failures the caller cannot reasonably ignore, and return `std::optional<T>` (or a `bool` plus out-parameter) for expected misses such as "not found".
- `ENMA-10.2` **MUST** throw types derived from `std::runtime_error` with a message that names what failed and the value involved.
- `ENMA-10.3` **MUST** catch by reference (`catch (const std::runtime_error& e)`), most specific first, with `catch (...)` last; Enma rejects misordered handlers.
- `ENMA-10.4` **MUST** catch only where you can recover or add context; otherwise let the exception reach the host, where `execute()` reports it.
- `ENMA-10.5` **MUST NOT** use exceptions for control flow in per-frame code.
- `ENMA-10.6` **MUST** treat null dereference and division by zero as bugs, even though they are catchable checked faults. Check first; do not write code that catches them on purpose.
- `ENMA-10.7` **MUST** remember that stack overflow, wild out-of-bounds writes and use-after-free that hits unmapped memory are **not** catchable: they end the run without unwinding.
- `ENMA-10.8` **MUST** check the result of every SDK call: `compile()` and `deserialize()` return `nullptr`, `execute()` and `call()` return `false` on failure.
- `ENMA-10.9` **MUST** report compile failures with `last_error()` — code, message, file, line and column — so the script author sees where the problem is.
- `ENMA-10.10` **MUST** keep the host running when a script fails: log the error, mark the script as failed, and carry on.
- `ENMA-10.11` **MUST** report faults from natives with `runtime_error(message)` (or `runtime_exception`) rather than C++ exceptions, and keep every native `noexcept` at its boundary.
- `ENMA-10.12` **MUST** call `mark_native_raises(e, name, false)` only for natives that genuinely can never raise; the per-call exception check is cheap insurance.

## 11. Modules, namespaces and the preprocessor

- `ENMA-11.1` **MUST** share code between files with `import`, not `#include`. Imported declarations keep their exact types and are reached through the module's namespace (`MathUtils::Clamp`).
- `ENMA-11.2` **MUST** import by module name (`import MathUtils`) when the module is on a host-registered module path, and by quoted relative path (`import "Lib/MathUtils.em";`) only for files that are not.
- `ENMA-11.3` **SHOULD** alias an import (`as Mu`) only to resolve a name clash or to shorten a deep path; never alias the same file twice.
- `ENMA-11.4` **MUST NOT** `#include` standard headers; the standard surface is always available. `#include` is only for sharing macros between your own files.
- `ENMA-11.5` **MUST** keep a module's private helpers `static` or in an anonymous namespace so they do not leak to importers.
- `ENMA-11.6` **MUST** keep each module's public declarations inside the namespace its file name gives it; do not add extra top-level namespaces in a module.
- `ENMA-11.7` **SHOULD** use `using namespace` only inside function bodies, and namespace aliases (`namespace Rd = Engine::Render::Detail;`) for long paths. A namespace alias cannot point at an imported module.
- `ENMA-11.8` **AVOID** macros in scripts. Use `constexpr` constants, `if constexpr` and functions instead.
- `ENMA-11.9` **MAY** use `#ifdef` on host-supplied defines (`define(e, "PRJ_DEBUG", "1")`) to compile debug-only checks.
- `ENMA-11.10` **MUST** prefix any macro with the host prefix (`PRJ_`), and keep macros in one shared include file.
- `ENMA-11.11` **MUST** ship scripts as `.emb` artifacts built in CI with `serialize()`, and load them with `deserialize()`, checking for `nullptr`.
- `ENMA-11.12` **SHOULD** build `.emb` files with `keep_debug = true` for development and internal builds, and `false` only for public releases where the source must not be recoverable.
- `ENMA-11.13` **MUST** rebuild every `.emb` when the SDK version changes.

## 12. Annotations and data layout

- `ENMA-12.1` **MUST** use layout annotations (`[[packed]]`, `[[align]]`, `[[offset]]`) only for types that cross a boundary (files, network, natives), with a comment naming the format they match.
- `ENMA-12.2` **MUST NOT** commit `[[noopt]]`.
- `ENMA-12.3` **SHOULD** add `[[reflect]]` to structs that host tools or editors inspect, instead of hand-written field tables.
- `ENMA-12.4` **MUST** keep `[[serialize]]` structs flat: no bit-fields, inline arrays or nested structs, and no pointers (their addresses would be written out).
- `ENMA-12.5` **MUST NOT** use `[[dll(...)]]` in gameplay or user scripts. FFI belongs in host natives, where it can be reviewed; scripts that need it are compiled only by engines granted `PERM_FFI`.
- `ENMA-12.6` **MUST** remember that the standard attributes (`[[nodiscard]]`, `[[likely]]`, `[[deprecated]]` and others) are parsed and **ignored**; do not rely on them to enforce anything.

## 13. Embedding: engine, module and context lifecycle

- `ENMA-13.1` **MUST** wrap the engine, each module and each context in an RAII owner in the host (`ScriptEngine`, `ScriptModule`, `ScriptContext`) that releases in reverse order: contexts, then modules, then the engine.
- `ENMA-13.2` **MUST** configure the engine completely — optimisation, debug, defines, include and module paths, resolvers, permissions, addons, natives — **before** the first compile. Configuration after compiling is a bug.
- `ENMA-13.3` **MUST** destroy every module and context before `destroy(engine)`; the engine does not track its children.
- `ENMA-13.4` **MUST** call `enma::shutdown()` once when the scripting subsystem is torn down; in a DLL that may unload, call it from `DLL_PROCESS_DETACH` and run no script code afterwards.
- `ENMA-13.5` **SHOULD** load scripts through `set_import_resolver` / `set_include_resolver` backed by the host's virtual file system, so packaged builds and development builds resolve the same way.
- `ENMA-13.6` **MUST** run entry points by name with `execute()` or `call()`, and read results with the matching `return_value`, `return_string` or `return_float`.
- `ENMA-13.7` **SHOULD** use `reload` for hot reload in development builds only, and drop every cached pointer into the old module (function addresses, annotation info, strings) after a reload.

## 14. Native functions and addons

- `ENMA-14.1` **MUST** register natives with `enma::register_typed<&Fn>(e, signature)` where possible, and wrap every signature in `ENMA_SIG("...")` so a malformed signature fails the host build instead of logging at run time.
- `ENMA-14.2` **MUST** use Enma's sized types in signatures (`int64`, `float64`, `const char*`) and keep them to plain values, pointers and references; signatures with `struct`, `class`, lambdas or varargs are refused.
- `ENMA-14.3` **MUST** keep every native function alive for as long as any module that calls it exists; the engine does not own `fn_ptr`.
- `ENMA-14.4` **MUST** validate every argument inside the native: natives are not protected by Enma's checks. Use `heap_is_tracked(ptr)` before trusting a pointer that claims to be script heap memory.
- `ENMA-14.5` **MUST** declare ownership: `mark_native_returns_borrow` for handles the host keeps, `mark_native_deallocates` for natives that free a parameter. Undeclared ownership is a leak or a double free waiting to happen.
- `ENMA-14.6` **MUST** gate natives that touch files, the network, processes or other unsafe areas with a permission flag at registration (`PERM_FILE`, `PERM_FFI`, or a host-defined bit), so scripts compiled without the grant fail to compile.
- `ENMA-14.7` **SHOULD** keep natives coarse: one call that does a whole job (`WorldSpawnWave(count)`) beats ten fine-grained calls per frame.
- `ENMA-14.8` **MUST** register only the addons a product needs (`register_addon_core`, `register_addon_std`, ...) instead of `register_all_addons` in shipping hosts, and disable the filesystem addon unless scripts truly need it.
- `ENMA-14.9` **MUST** build host-specific capabilities (networking, telemetry, game systems) as custom addons with their own namespace, not as loose natives, when they have more than a handful of functions.
- `ENMA-14.10` **MUST** document every addon's functions, permission and threading rules in its header and on the docs site.
- `ENMA-14.11` **MUST NOT** expose anything from an addon that lets a script register further natives or change permissions.

## 15. Threading and contexts

- `ENMA-15.1` **MUST** compile on one thread per engine at a time. Concurrent `compile`, `link`, `reload` or `deserialize` calls on one engine are refused; use one engine per compiling thread or serialise compiles.
- `ENMA-15.2` **MUST** give every thread that runs a module its own context from `create_context(mod)`, created before the thread starts and destroyed after it is joined.
- `ENMA-15.3` **MUST NOT** share a context between threads at the same time; a second thread entering a busy context is refused. Hand a context to another thread only after the first has finished and been joined.
- `ENMA-15.4` **MUST** treat script globals as shared by every context of a module. Guard them with `aint*` types or a `mutex` + `lock_guard` from the Thread addon, or write them from one thread only.
- `ENMA-15.5` **MUST** open an `execution_scope` on host threads that call into script memory outside a run (allocating on the script heap, invoking a script function pointer, raising an exception).
- `ENMA-15.6` **SHOULD** give workers that need their own budget, breakpoints or globals their own module; budgets and debug state are per module, not per context.
- `ENMA-15.7` **MUST NOT** use script threads (the Thread addon) for work the host can schedule; prefer host-side job systems that call into contexts.
- `ENMA-15.8` **MUST** remember that async/await, futures and coroutines do not exist in Enma; long work is split across frames or moved to a worker context.

## 16. Safety: permissions, budgets and containment

- `ENMA-16.1` **MUST** start every engine with `set_permissions(e, 0)` and grant flags explicitly, per engine, for the scripts that engine compiles.
- `ENMA-16.2` **MUST** compile untrusted scripts (user content, mods, downloaded code) on a separate engine with no permissions, no filesystem addon and only the natives they are meant to have.
- `ENMA-16.3` **MUST** remember that permissions check what a module binds, not what its machine code does: never load `.emb` files from untrusted sources without checking their origin, since they are native code.
- `ENMA-16.4` **MUST** set an instruction budget (`set_budget`) and a heap budget (`set_memory_budget`) on every module that runs untrusted or user-editable scripts, and treat exceeding them as a failed run.
- `ENMA-16.5` **SHOULD** set budgets from the host, not from the script; a script can call `set_budget` itself, so untrusted scripts must not be able to raise their own limits (do not rely on scripts calling it).
- `ENMA-16.6` **SHOULD** size budgets from measurements of real content plus headroom, and log the measured use in development builds.

## 17. Performance

- `ENMA-17.1` **MUST** measure before optimising: time with `time_ms()` or `__asm_rdtsc()` in scripts, and the host profiler around `execute()` / `call()`.
- `ENMA-17.2` **SHOULD** compile modules once and cache them (or ship `.emb` files); `compile()` is far more expensive than running.
- `ENMA-17.3` **SHOULD** create contexts once per worker and reuse them, rather than creating one per call.
- `ENMA-17.4` **SHOULD** keep per-frame script code free of heap allocation: reuse vectors (`clear()` keeps capacity), pass by `const&`, and avoid building strings unless they are printed.
- `ENMA-17.5` **SHOULD** batch native calls: one call that processes an array is cheaper than one call per element.
- `ENMA-17.6` **MAY** use the SIMD and Math:3D addons, `[[align(16)]]` data and `[[inline]]` on hot paths, after a profile shows the need.
- `ENMA-17.7` **SHOULD** use `hold_compile_caches(true)` around a batch of compiles (for example, loading a level's scripts), paired with exactly one `hold_compile_caches(false)`.
- `ENMA-17.8` **AVOID** deep template instantiation in hot modules; every instantiation is separate machine code, which grows the module and compile time.

## 18. Perception: scope, script structure and lifecycle

- `ENMA-18.1` **MUST** use Perception scripts only with single-player, offline games and your own software. [MUST NOT] attach to games with online play, shared leaderboards or anti-cheat, or to any process you do not have the right to modify.
- `ENMA-18.2` **MUST** check the target at startup (process name and module) and unload if it is not the expected single-player title.
- `ENMA-18.3` **MUST** do one-time work (attaching, pattern scans, font creation, shader and buffer creation) in `main()`, never in a routine.
- `ENMA-18.4` **MUST** return `0` from `main()` with a logged reason when setup fails (process not found, pattern not found), instead of leaving routines running against bad state.
- `ENMA-18.5` **MUST** keep each routine short and non-blocking; split slow work across frames.
- `ENMA-18.6` **MUST** treat routines as concurrent: they may run at the same time and share globals, so shared mutable state is atomic (`aint64`) or behind a `mutex`.
- `ENMA-18.7` **MUST** restore every value a script wrote to the target in `unload()`, so unloading leaves the game as it was.
- `ENMA-18.8` **SHOULD** keep each routine's handle and unregister it when its feature is turned off, rather than branching on a flag every frame.

## 19. Perception: process access and permissions

- `ENMA-19.1` **MUST** request only the permissions a script uses, and state why at the top of the script in a comment.
- `ENMA-19.2` **MUST NOT** use `kernel_rw_access`, DTB overrides or physical-memory reads and writes. If a target only works through them, it has protections you should not be working around.
- `ENMA-19.3` **MUST** check `alive()` before using a stored `proc_t` in a routine; the game may have closed.
- `ENMA-19.4` **MUST** check every pointer read from the target with `is_valid_address` before following it, and treat a zero or invalid pointer as "not ready", not as an error to retry every frame.
- `ENMA-19.5` **MUST** locate code and data with `find_code_pattern` (byte signatures with `??` wildcards) and module-relative offsets, never with absolute addresses.
- `ENMA-19.6` **MUST** keep signatures and offsets in one table at the top of the script (or a shared `Offsets.em` module), each with the game version it was verified on, and fail closed in `main()` when any of them is not found.
- `ENMA-19.7` **SHOULD** read structs in one bulk call (`rvm`) instead of many single reads, and cache values that do not change every frame.
- `ENMA-19.8` **MUST** write the smallest change possible, remember the original value, and restore it in `unload()` and when the feature is turned off.

## 20. Perception: rendering and overlays

- `ENMA-20.1` **MUST** create fonts, bitmaps, shaders and buffers once in `main()` and reuse them. Load each font at the size it is drawn; fonts and bitmaps are cached for the whole process and never freed.
- `ENMA-20.2` **MUST** check every `create_*` / `load_*` handle: `0` means failure. Read `get_last_render_error()` and fail setup with its message.
- `ENMA-20.3` **MUST** use `color(...)` for RGBA values and `vec2(...)` for positions and sizes; keep shared colours as named constants (`Colors::Text`, `Colors::Warning`).
- `ENMA-20.4` **SHOULD** batch similar shapes with the batch forms (`draw_rects`, `draw_lines`) instead of one call per item.
- `ENMA-20.5` **SHOULD** upload static geometry once to resident buffers and draw it with `custom_draw_buffer*`; use dynamic buffers only for data that changes every frame.
- `ENMA-20.6` **MUST** set pipeline state (viewport, blend, targets) right before each custom draw, and end every custom sequence with `custom_restore_state()`; state does not carry over between draws or routines.
- `ENMA-20.7` **SHOULD** keep overlays readable and out of the way: one corner by default, outlined or shadowed text (`effect` 1 or 2), and a toggle to hide everything.
- `ENMA-20.8` **SHOULD** measure overlay cost with `get_render_thread_frame_ms()` and GPU timers, and keep a script's drawing well under a millisecond per frame.
- `ENMA-20.9` **MAY** build settings panels with the GUI API rather than hand-drawn widgets, so every script's controls look and behave the same.

## 21. Perception: API conventions and tooling

- `ENMA-21.1` **MUST** use `static_cast<T>(x)` for numeric conversions and `reinterpret_cast<int64>(fn)` to pass a function as a routine or callback handle.
- `ENMA-21.2` **MUST** suffix `float32` literals with `f` (`0.2f`, `40.0f`); a bare `0.2` is `float64`.
- `ENMA-21.3` **MUST** treat values from `create_*` and `load_*` as opaque handles and pass them only to the matching API.
- `ENMA-21.4` **MUST** use `std::string` for owned text and `const char*` for read-only parameters; convert numbers with `std::to_string` or `std::format`.
- `ENMA-21.5` **MUST** use `std::vector<T>` for dynamic storage and `std::array<T, N>` for fixed-size storage.
- `ENMA-21.6` **MUST** use `print_console` for chatty debug output so the main log stays readable, and `println` for lifecycle messages.
- `ENMA-21.7` **MUST** edit scripts with the Perception language server enabled (diagnostics, hover, completion, go-to-definition) and fix every diagnostic before loading a script.
- `ENMA-21.8` **MAY** use the Perception MCP interface to let an AI agent load, run and inspect scripts locally; review any agent-written script against this guide before running it against a game.
- `ENMA-21.9` **SHOULD** keep each script in its own folder with a short `README.md`: the target game and version, the permissions it requests and why, and how to turn each feature off.

## 22. Testing

- `ENMA-22.1` **MUST** keep tests beside the script as `<Name>.test.em`, importing the module under test.
- `ENMA-22.2` **MUST** write each test as an `int64 Test<Behaviour>()` function that returns `0` on success, using the `assert(cond, msg)` builtin for checks. The host's test runner finds and calls every `Test*` function.
- `ENMA-22.3` **MUST** check for leaks: record `heap_count()` before and after each test and fail if it changed.
- `ENMA-22.4` **MUST** test error paths too: that the right exception is thrown, and that `.at()` throws on a bad index.
- `ENMA-22.5` **SHOULD** run tests on an engine configured like production (same addons, permissions and defines) plus a tighter budget, so runaway loops fail fast.
- `ENMA-22.6` **MUST** test every native with good, boundary and bad arguments, from a script, and check that bad arguments raise instead of crashing.
- `ENMA-22.7` **MUST** test that a script without a permission fails to compile when it calls a gated native.
- `ENMA-22.8` **MUST** test budgets: an infinite loop and a runaway allocation each fail `execute()` while the host keeps running.
- `ENMA-22.9` **SHOULD** run host and script tests under the same CI job on Windows x64, with ASan builds of the host where the SDK allows it.
- `ENMA-22.10` **SHOULD** compile every `.em` file and round-trip it through `serialize` / `deserialize` in CI to catch artifact problems before release.

## 23. Git, releases and review

- `ENMA-23.1` **MUST** follow **Conventional Commits**: `type(scope): summary`, imperative, lower case, no trailing period, under 72 characters. Use the module or host area as scope: `feat(spawner): add wave cooldown`, `fix(natives): validate key range`.
- `ENMA-23.2` **MUST** mark breaking changes to natives, addons or module APIs with `!` and a `BREAKING CHANGE:` footer.
- `ENMA-23.3` **MUST** keep commits small and focused, each compiling every script and passing tests.
- `ENMA-23.4` **MUST** record the SDK version in the host's release notes, and rebuild every `.emb` artifact for each release.