Step One — Set

Standards

The orders the work is done under.

Tiger Style is Qompass AI's standing orders for code. Safety first, everything bounded, nothing hidden — in every language we ship.

These standards come first. They are the terms under which work is judged: how claims are weighed in Analysis, what counts as a result worth recording in Field Notes, and what may stand. When promoted Research lands, it returns here — and these standards are revised.

RESPECT WHERE DUE

This standard descends from TigerStyle, the coding standard written for TigerBeetle — a database that cannot afford to be wrong — and, behind it, NASA JPL's Power of Ten, Gerard J. Holzmann's rules for developing safety-critical code. It is adapted here with respect, and the debt is stated plainly.

What Qompass takes from both: determinism over cleverness — explicit limits, bounded work, deterministic interfaces. A program should verify itself while it runs, and shut down rather than run wrong. And doing it right the first time, because production rarely grants a second draft.

THE ORDERS THEMSELVES

Safety, then performance, then convenience

The priority order, always. When the three compete — and they compete daily — safety wins outright, performance is argued with measurements, and developer experience takes what is left.

SAFETYPERFORMANCEDEVELOPER EXPERIENCE

Priority order, always: Safety > Performance > Developer Experience.

— the Rust guide, below

Contracts first

Every substantial operation answers, before it is written: accepted and rejected inputs, the trust boundary, the maximum work, memory, output, and time, the owner of every allocation and handle, and what failure and cancellation do. If the contract cannot be stated, the operation is not understood yet — and is not written yet.

OPERATIONINPUTS →→ BOUNDS→ OWNER→ FAILURE

Bound everything

Limits are named constants carrying units — output_bytes_max, retry_count, queue_capacity, deadline — and index ranges are validated with checked arithmetic before an end is computed. A bound that lives only in someone's head is not a bound.

MAXwork grows to the limit — and stops there, by construction

No unbounded loops, retries, queues, or growth.

— the Rust guide, below

Errors are data; assertions are law

Invalid input, configuration, or protocol data gets a typed error with bounded context; the caller owns recovery. A corrupt internal invariant gets an assertion — the program verifies itself while it runs and stops rather than run wrong. Nothing unwraps external data, and no required mutation hides inside an assertion.

bad input ───→ typed error ───→ caller recoversbroken invariant ───→ assertion ───→ halt, loudly

Paired tools

No guide ships alone. Each names its toolchain — language server, formatter, linter, debugger — so the standard is enforced by machines, not taste. The pairing is in every chapter below: rust-analyzer, clippy, and rustfmt for Rust; zls and zig fmt for Zig; shellcheck and shfmt for Bash.

LSP — see the contractFORMAT — one shapeLINT — zero warningsDEBUG — inspect, don't guess

THE ORDERS, BY LANGUAGE

Seventeen chapters — the operational core of each language's guide, verbatim. Open one and read it here; the full reference behind each chapter is one link further, on its own page.

AsepriteSafety-first Aseprite — headless CLI and Lua pipelines with explicit contracts, version guards, and every flag verified against official docs.

Tiger Style Aseprite

Prerequisite: tiger-style-lua (sibling skill at ../tiger-style-lua/SKILL.md) — apply it first to every Lua script this skill produces or reviews, including Lua embedded in generated Aseprite scripts.

Purpose

Automate Aseprite the way its docs describe it: the CLI for exports and introspection, Lua scripts for pixel-level work, always in batch mode for headless pipelines. Safety order, always: Safety > Performance > Developer Experience. Every flag and API member used here was verified against the official docs on 2026-09-27; anything the docs do not say is marked unverified. references/aseprite-cli.md and references/aseprite-scripting-api.md hold the complete verified tables — this file is the operational core.

Official sources:

Workflow

  1. Detect the binary and probe it. Never assume aseprite is installed, on PATH, or the right version.
  2. Prefer direct CLI flags when they cover the job (--save-as, --sheet, --tag, --split-layers, --scale); reach for --script only when pixel-level logic is required.
  3. Run everything in batch mode (-b). Guard every script with the batch contract below.
  4. Write generated Lua to a temp file, invoke with an argv array, enforce a bounded timeout, check the exit code, clean up the temp file.
  5. Version-guard every API feature younger than the minimum supported Aseprite with app.apiVersion. Never use a deprecated alias.
  6. Treat LibreSprite as a different product: consult the divergence table in references/aseprite-scripting-api.md before running any Aseprite-1.3 script or flag against it.

Operating Rules

Binary detection and probing

  • Search, in order: ASEPRITE_BIN, then NVIM_ASEPRITE_BIN (diver convention), then executable candidates on PATH. Record which one won and report it.
  • Probe with an argv array, never a shell string: { bin, '--version' }. Bound stdout (a few KB) and time (10 s).
  • Parse the version from stdout; compare with Version semantics. Require Aseprite ≥ 1.3 for any feature listed as v1.3+ in the references, and LibreSprite ≥ 1.1 only for the LibreSprite-safe subset.
  • If no binary is found, stop and say so. Do not download Aseprite silently; a download needs explicit approval and a checksum/signature note.

CLI invocation contract

  • Always an argv array (one element per argument), never an interpolated shell string. Paths with spaces stay single elements.
  • Always -b (batch) for automation. GUI launches need explicit approval.
  • Option order matters. Selection/export options (--tag, --frame-range, --layer, --ignore-layer, --all-layers, --split-layers, --split-tags, --split-slices) apply to the next sprite opened on the command line: place them before the .aseprite file. --script-param name=value goes before --script script.lua. --scale affects sprites opened before the option.
  • Bound every run: an explicit timeout (default 120 s for exports; longer only with a named reason) and bounded stdout/stderr capture.
  • Exit-code contract: 0 means success. Any non-zero exit, or a Lua error printed to stderr, means the run failed — even if some output files exist. Verify expected output files exist after a 0 exit before reporting success.
  • --sheet overwrites its output file. --data "" prints sheet JSON to stdout. --save-as frame001.png writes a numbered PNG sequence.
  • Temp-file hygiene: generated scripts go to a temp file with a distinctive suffix (e.g. _diver_bgremove.lua), and are removed after the run whether it succeeds or fails.
  • Do not pass --version-unverified flags. The full verified flag table is in references/aseprite-cli.md; notably --split-grid and --export-tileset require v1.3-beta21+, and fractional --scale factors are unverified (the docs show integer examples only).

Lua script contract (batch mode)

Every script run via -b --script must satisfy all of these:

  1. No UI, ever. app.isUIAvailable is false in batch mode. Dialog() returns nil there (since v1.2.35). Do not call Dialog, app.alert, or app.tip — report status with print() to stdout and failures with error() or os.exit(1) (non-zero exit is the failure signal).
  2. Input through app.params. CLI --script-param name=value pairs arrive as strings in app.params. Validate and coerce every param at the top of the script; fail loudly on missing/invalid values.
  3. app.sprite can be nil. Check it before touching it. When the CLI opens the input file (-b input.aseprite --script s.lua), that sprite becomes active — but never assume it.
  4. Color mode first. Pixel math with app.pixelColor.rgbaR/G/B/A assumes RGBA. Read sprite.colorMode and compare against ColorMode.RGB before interpreting pixels; indexed images hold palette indices, not channel bytes. RGB components are not premultiplied by alpha.
  5. Transactions for edits. Wrap document mutations in app.transaction([label], function() ... end) — it groups undo and rolls back the whole change on error. Since v1.3.16, property changes outside a transaction do not generate undo steps, so edits that must be undoable (or atomic) belong inside one.
  6. No destructive action without confirmation. sprite:close() does not prompt; saveAs() marks the document saved and retargets it. Scripts that must not disturb the open document use saveCopyAs(). In interactive contexts, confirm before overwriting user files; in batch pipelines the explicit output path in the invocation is the confirmation — and it must be named, never defaulted to the input path.
  7. Version guards. Use app.apiVersion (number; nil on the very first scripting builds) for API features, and app.version (a Version object) for app behavior. The per-version feature table is in references/aseprite-scripting-api.md. Never use a deprecated alias (app.activeSprite, app.activeLayer, app.activeFrame, app.activeCel, app.activeImage, app.activeTag, app.activeTool, app.activeBrush, putPixel/putImage/putSprite).
  8. Stdlib limits. Aseprite embeds Lua 5.3 libraries; os.exit and os.tmpname are unavailable, and os.execute/io.open may ask the user for permission — avoid them in batch scripts.
  9. Deterministic pixel work. For raw pixels prefer Image:pixels() iteration and the clone→modify→cel.image = img pattern (the image setter is the documented single-undoable-action replacement) over app.useTool, which depends on thin batch-mode editor state.

LibreSprite boundary

LibreSprite 1.1 is not API-compatible with Aseprite 1.3 scripts:

  • Different app surface: app.activeSprite/app.activeLayerNumber/ app.activeImage/app.activeFrameNumber (the names Aseprite deprecated), app.createDialog() instead of Dialog(), app.version returns a string, and there is no documented app.apiVersion, app.params, or app.isUIAvailable.
  • Different Image: only getPixel/putPixel, getImageData/ putImageData, getPNGData, clear(color) — no pixels() iterator, no drawPixel, saveAs, resize, flip, or shrinkBounds.
  • Different Sprite: saveAs(fileName, asCopy), commit(), layer(n) (0-indexed from bottom), no saveCopyAs, no transactions named as such.
  • No --script-param in its scripting docs. CLI batch/script support (-b, --script) is corroborated by third-party source reading, not official LibreSprite docs — verify with libresprite --help on the real binary before relying on it.

Write a compat shim (feature-detect, never version-sniff LibreSprite) when one pipeline must serve both, or keep two scripts. The full divergence table is in references/aseprite-scripting-api.md.

Diver wiring (games/aseprite)

  • Binary lookup: ASEPRITE_BIN, NVIM_ASEPRITE_BIN, then PATH candidates (autil.find_binary()).
  • Established export argv shapes: -b <sprite> --sheet <png> --data <json> --format json-array; --save-as for PNG sequences, GIF, and palette output; --sheet-type packed|rows|columns|horizontal|vertical.
  • The AI import pipeline generates a background-keying Lua script run as { bin, '-b', input, '--script', script_path }. Known gaps to fix when touching it: the generated script calls app.alert() on a nil sprite (invalid in batch mode — replace with print + non-zero exit) and assumes RGBA pixels without checking sprite.colorMode. Palette quantization is manual on the Aseprite path (ImageMagick fallback does keying + quantize + point filter, bounded at 64 files / 16 MB / 120 s).
  • Default config: 128 px frames, background-key options, palette size, auto-open behavior, scale_via_cli. Fractional --scale is unverified — keep integer factors until the docs say otherwise.

Review checklist

  • Binary detected and version probed; the winning path is reported.
  • Every CLI flag is in references/aseprite-cli.md (or verified in the docs for the probed version).
  • argv arrays everywhere; bounded timeout and output; exit code checked; output files verified after exit 0.
  • Options ordered correctly (selection filters before the input file; --script-param before --script).
  • Script has no UI calls in batch mode; input via app.params, validated; status via print, failure via non-zero exit.
  • app.sprite nil-checked; colorMode checked before pixel math.
  • Mutations inside app.transaction; saveCopyAs used where the open document must be undisturbed.
  • app.apiVersion guards on every post-1.3-beta API feature; no deprecated aliases.
  • LibreSprite divergence consulted if the target might be LibreSprite.
  • Temp scripts removed after the run, success or failure.

ReferencesAseprite · Aseprite CLI docs · Aseprite scripting docs

BashSafety-first Bash — strict mode, every expansion quoted, bounds on every loop and retry, and exit statuses checked, never assumed.

Tiger Style Bash

Purpose

Apply the Tiger Style standard to Bash scripts. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_BASH.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Declare the interpreter and version up front: #!/usr/bin/env bash plus a runtime version guard (BASH_VERSINFO). Never write bashisms in a #!/bin/sh script — pick one and be honest about it.
  2. Open every script with the strict-mode header (see below). Lay out top-to-bottom: header comment (purpose, usage, exit codes), strict mode, constants, helpers, main() with "$@", and the main "$@" invocation guard.
  3. Quote every expansion: "$var", "${arr[@]}", "$(cmd)". An unquoted expansion is a bug until proven otherwise.
  4. Bound everything with named UPPER_SNAKE_CASE constants: timeouts, retry counts, bytes read, loop iterations.
  5. Review against the checklist below before calling the script done.

Operating Rules

  • Strict mode, honestly. set -euo pipefail is the starting point, not a safety proof: -e does not fire inside if conditions, &&/|| lists, or the last command of a pipeline's context in some versions. Check the exit status of anything that matters explicitly. set -u turns unset variables into errors — initialize or default (${VAR:-default}) deliberately.
  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, exit codes, failure behavior. A function communicates status via its return code and data via stdout — never mix the two.
  • Quoting. "$var" always. "${arr[@]}" for arrays, never $arr or ${arr[*]} unquoted. read -r always (raw backslashes). IFS= prefix when reading whole lines. Never for x in $(cmd) — use while IFS= read -r loops or mapfile.
  • No eval. No eval, no source on computed paths, no curl | bash. If a value must become code, that is a design failure — restructure. Verify checksums/signatures on downloaded artifacts before executing them.
  • Temp files: mktemp only, never predictable /tmp names (symlink attacks). Register cleanup with trap 'rm -rf "$TMPDIR"' EXIT at creation time. One trap per concern; append, don't overwrite.
  • Globbing: never parse ls. Set nullglob/failglob deliberately and document the choice. Quote globs you mean literally.
  • Errors: a die() helper (printf ... >&2; exit 1) for fatal errors. Distinguish exit codes: 0 success, 1 general error, 2 usage error. Log to stderr; data to stdout. Never swallow a failure with || true unless you can state what you are ignoring and why.
  • Pipelines: pipefail so a mid-pipe failure is not masked by the last command's success. Capture pipeline status explicitly when the stages have different failure meanings (PIPESTATUS).
  • Bound everything. timeout on network/subprocess calls, head -c on unbounded reads, read -t on interactive input, --max-count on searches. No unbounded find / or recursive globs without depth limits.
  • Filesystem: mkdir -p before writing; check with -e/-f/-d before acting on paths from outside. Use -- to end option parsing before user-supplied paths (rm -- "$f"). Never rm -rf an unvalidated variable — assert it is non-empty and under an expected prefix first.
  • Environment: treat the environment as untrusted input. PATH manipulation must be explicit and logged. Never trust CDPATH, IFS, or LD_PRELOAD across a privilege boundary.
  • Portability: if the script must run on macOS/BSD too, avoid GNU-only flags or gate them behind uname checks. Document the supported platforms in the header.
  • Functions: local for every function variable. Functions stay near or below 70 lines. One purpose per function; helpers prefixed consistently.
  • Formatting: shfmt -i 4 -ci (4-space indent). One formatter, no hand-formatting fights. shellcheck must report zero warnings — suppressions (# shellcheck disable=) require a comment explaining why the warning is wrong, not just noisy.
  • Testing: bats for behavior tests; test the failure paths, not just the happy path (half-adversarial). Test with shellcheck clean as a gate.
  • Comments explain why, not what. Header carries the contract: purpose, usage, arguments, exit codes, side effects, required tools.

Output Contract

Bash you produce must: open with #!/usr/bin/env bash and the strict-mode header, quote every expansion, use local in all functions, create temp files only via mktemp with trap cleanup, never eval or pipe curl to bash, bound all reads/loops/retries/timeouts with named constants, separate data (stdout) from diagnostics (stderr), pass shellcheck with zero warnings and shfmt clean, and pass the review checklist.

Review Checklist

  • #!/usr/bin/env bash, version guard, strict-mode header present.
  • Every expansion quoted; read -r; no for x in $(...).
  • No eval, no source on computed paths, no curl | bash.
  • Temp files via mktemp with trap cleanup registered at creation.
  • Exit codes documented and deliberate; die() for fatal errors; no unexplained || true.
  • pipefail set; PIPESTATUS checked where stage failures differ.
  • All reads, loops, retries, timeouts bounded by named constants.
  • rm -rf targets validated non-empty and under expected prefix; -- before user paths.
  • Functions use local; ≤ ~70 lines; single purpose.
  • shellcheck zero warnings; shfmt clean; bats tests cover failure paths.

ReferencesBash manual · ShellCheck

CSafety-first C — explicit contracts, checked bounds, and disciplined manual ownership, hardened against overflow and use-after-free.

Tiger Style C

Purpose

Apply the Tiger Style standard to C code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_C.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Declare the contract up front: C standard (C17; gate C23 features on a feature check, not a compiler flag), compiler versions, data model, target arch, libc, and required POSIX/Linux APIs. Isolate platform adapters from portable domain code.
  2. Lay the module out: small public header for types/contracts, one implementation file for static private helpers. Include guards, self-contained headers, explicit standard includes, one clear public API prefix. No surprising mutable global state from headers.
  3. Validate external input with a documented status enum plus output parameters; assert internal invariants with assert()/_Static_assert (one invariant per assert). Never validate only inside assert — NDEBUG strips it, and external checks must survive release builds.
  4. Bound everything with named *_MAX constants: loops, retries, queues, sizes, nesting depth, cumulative bytes. Check size multiplication (count <= SIZE_MAX / size) before allocating.
  5. Compile with strict warnings (-Wall -Wextra -Wpedantic -Wconversion -Wshadow), run under ASan/UBSan, test again with -DNDEBUG, then review against the checklist below.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior, whether (NULL, 0) is valid, and whether outputs stay unchanged on failure.
  • Assertions are for invariants, never for external input. _Static_assert for compile-time representation constraints; assert() for runtime invariants. Never acquire resources, validate, or mutate state only inside them.
  • Errors are status enums, not prose. Prefer leaving outputs unchanged until success. Capture errno right after the failing call, before cleanup can clobber it; distinguish library status from OS errno. Handle partial reads/writes, EOF, interruption — and never blindly retry close after EINTR on Linux (the descriptor may already be released and reused).
  • Bound everything. No unexplained while (1), no unbounded retry, no unbounded growth, no VLAs for external sizes. Retries require an idempotent operation, a shared deadline, and bounded backoff.
  • Control flow: straightforward branches, bounded loops, exhaustive handling of accepted states. A goto cleanup label is acceptable (local labels, monotonic ownership) when it beats duplicated cleanup. No recursion over attacker-controlled depth; no macros that hide control flow or evaluate arguments twice.
  • Absence discipline: a null pointer, an empty valid span, a missing optional value, and an invalid argument are four different things. State the (NULL, 0) policy per API. Never call a memory function with null unless its contract permits it; memset is not type-aware zero-initialization.
  • Names are snake_case functions/fields, UPPER_SNAKE_CASE macros, units last: input_count, output_capacity_bytes. One public prefix; never use reserved identifiers (leading _+uppercase, __). Prefer explicit structs/enums over opaque typedef ownership tricks.
  • Formatting: 4-space indent, max 100 columns, one statement per line, braces for multi-step control flow. Pinned clang-format: BasedOnStyle: LLVM, IndentWidth: 4, ColumnLimit: 100, UseTab: Never. Never reformat vendored code.
  • Functions: ordinary functions stay near or below 70 lines. Signatures carry explicit buffers, capacities, statuses, and ownership. No positional flag piles; document aliasing/overlap policy per API.
  • Ownership: one owner per mutable buffer, fd, and handle. One cleanup path per acquisition sequence, released in reverse order; record ownership immediately after acquisition. Never return a pointer into automatic storage or retain a borrowed pointer past its owner's release. Freeing one owner pointer does not invalidate aliases — clear the owner and document it. Teardown must document how it detects already-released state.
  • Security: fixed format strings for untrusted text; bounds-check every copy (strncpy is not a safe strcpy); cast non-EOF bytes to unsigned char for ctype functions. Treat pointer arithmetic, casts, FFI, format strings, dynamic loading, and shell/process boundaries as review points. Use argv form; never system()/popen() for externally influenced commands. Treat sanitizer findings as real defects until disproved.
  • Determinism: no uninitialized bytes or struct padding to hashes, logs, or the wire; explicit byte encodings and stable ordering; fix locale and encoding assumptions for parsing; rand() is not a security primitive.
  • Comments explain why and state contracts: pointer validity, size units, overlap, thread safety, error outcomes, output mutation, ownership transfer, and whether an interface is ISO C, POSIX, or Linux-specific.
  • Concurrency: documented pthread/C11-atomic protocol; volatile is not synchronization; data races are UB. Signal handlers must be async-signal-safe; define shutdown, wakeup, cancellation, join, and buffer lifetime before workers start.
  • Numbers: size_t for object sizes, fixed-width types for protocol fields, correct format macros. Signed overflow is UB; unsigned wrap is defined but rarely the right size policy. Decode endianness explicitly — never cast packet bytes to structs. Never serialize raw struct bytes as a portable wire format.

Output Contract

C you produce must: compile cleanly under strict warnings for the declared standard, document contracts in public headers, return status enums on expected failures with outputs unchanged, assert its invariants, bound all loops/retries/sizes, pass clean under ASan/UBSan and with -DNDEBUG, stay within 100 columns and ~70 lines per function, and pass the review checklist.

Review Checklist

  • External input validated with status returns in every build; invariants asserted, never validation-inside-assert.
  • All loops/retries/sizes/depths bounded by named constants; size arithmetic checked before allocation.
  • Every allocation result checked; realloc via a temporary; (NULL, 0) policy stated per API.
  • Null/empty/missing/invalid distinguished; no null passed to memory functions unless the contract permits.
  • Every resource has one owner; one cleanup path covers all failure paths; no pointer into automatic storage escapes.
  • No system()/popen() or shell interpolation for external input; fixed format strings; argv form used.
  • No uninitialized bytes or padding reach hashes/logs/wire; encoding and locale assumptions explicit.
  • Integer edges, partial I/O, interrupted syscalls, and allocation failure tested; sanitizers clean; tests pass under -DNDEBUG.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, clang-format clean, comments explain why and state contracts.

ReferencesC reference (cppreference) · clangd

C++Safety-first C++ — C++23 with contracts first, std::expected for validation, Rule-of-Zero RAII ownership, and one explicit exception policy.

Tiger Style C++

Purpose

Apply the Tiger Style standard to C++. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_CPP.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Declare the profile up front: language standard (C++23 reference; C++20 needs an explicit expected-result alternative), compiler, standard library, exception policy, and target platform.
  2. Lay the module out top-to-bottom: purpose comment, narrow public header with domain types and contracts first, private implementation helpers, Rule-of-Zero value types where possible.
  3. Validate external input with std::expected<T, E>; assert internal invariants with static_assert (compile-time) and assert() (runtime — remember NDEBUG removes it).
  4. Bound everything with named constexpr constants: item counts, byte sizes, nesting depth, retries, queues, captured output.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every public operation answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior (unchanged / valid-but-changed / partial — documented).
  • Errors: one exception policy plus one typed expected-error policy per subsystem. std::expected<T, E> carries routine domain failure — check state before touching value or error. Never let an exception cross a C ABI boundary; translate it there.
  • Assertions are for programmer errors, never for external input. No side effects inside assertions. A std::span is a borrow, not a bounds check — prove indices before access.
  • Bound everything. No unexplained while (true), no unbounded retry, no unbounded growth. Retries require an idempotent op and a shared deadline; name the budgets (item_count_max, input_bytes_max).
  • Ownership: RAII and the Rule of Zero. unique_ptr for exclusive heap ownership; shared_ptr only for genuine shared ownership, cycles broken deliberately. Destructors are nonthrowing. Fallible completion (flush, commit) gets an explicit checked finish before cleanup.
  • Views borrow, never own. std::string_view / std::span do not extend lifetime; keep them in the smallest useful scope; never return references to temporaries or locals. Capturing this in a lambda keeps nothing alive.
  • Numbers: signed overflow is UB; unsigned wrapping is not a capacity policy. Check before conversion and arithmetic (value > total_max - total before +=). No C-style casts, no silent negative-to-size_t conversion.
  • Absence: std::optional for absence, std::expected for failure — never a null pointer or magic integer carrying several meanings. Moved-from objects are valid but unspecified; don't assume "empty".
  • Names: snake_case for functions/variables, PascalCase for domain types, units last: payload_size_bytes, timeout as std::chrono durations. No n, tmp2, reserved identifiers, or ownership-hiding prefixes.
  • Formatting: pinned clang-format — BasedOnStyle: LLVM, IndentWidth: 4, ColumnLimit: 100, UseTab: Never. Includes organized, headers self-contained; run semantic rewrites separately from formatting.
  • Functions: ordinary functions stay near or below 70 lines. Narrow interfaces, explicit result types, domain option structs over boolean flag lists.
  • Concurrency: prefer std::jthread with an explicit stop-token protocol — stopping is cooperative, not forced. Data races are UB; volatile does not synchronize. Coroutine frames need a clear owner.
  • Security: treat reinterpretation, raw pointers, C casts, format strings, and dynamic loading as trust boundaries. Build with warnings plus ASan/UBSan (-Wall -Wextra -Wpedantic -fsanitize=address,undefined).
  • Determinism: never serialize raw object representation (padding, vptrs, endianness); unordered-container iteration must not define canonical output; inject clocks/randomness in tests.
  • Comments explain why (lifetimes, why a cast is valid, exception guarantees, complexity bounds), not what. A noexcept must be justified across every called operation.

Output Contract

C++ you produce must: compile cleanly under the declared profile, carry documented contracts on public operations, assert its invariants, return std::expected on expected failures, bound all loops/retries/queues, give every resource one RAII owner with failure-path teardown, stay within 100 columns and ~70 lines per function, and pass the review checklist.

Review Checklist

  • External input validated before use; internal invariants asserted; release (NDEBUG) behavior tested separately.
  • All loops/retries/queues/allocations bounded by named constexpr constants.
  • Ownership explicit on success and every failure path; no new/delete, no dangling views or captures.
  • std::expected checked before value/error access; exception guarantees documented; no throw across C ABI.
  • Arithmetic checked before conversion/overflow; units explicit in names and types.
  • No shell construction from untrusted input; argv form used; no std::system on external inputs.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, clang-format clean, comments explain intent.

ReferencesC++ reference (cppreference) · C++ Core Guidelines

GoSafety-first Go — errors as values, panic reserved for broken contracts, and goroutines, queues, and retries bounded by named limits.

Tiger Style Go

Purpose

Apply the Tiger Style standard to Go code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_GO.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Record the module's Go/toolchain version, GOOS/GOARCH, and cgo policy; never assume 64-bit int, scheduling order, or tested cross-compiles.
  2. Lay the package out top-to-bottom: header/purpose, constants, types, constructors/validators, operations, tests. Use cmd/<program> for executables, internal/ for implementation boundaries; avoid init() side effects.
  3. Validate external input with explicit error returns; reserve panic for broken programmer contracts and irrecoverable internal state.
  4. Bound everything with named Max constants: input bytes, items, nesting depth, workers, queue entries, retries, output capture.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior. Public operations follow input → validation → bounded computation → validated change → commit → cleanup.
  • Errors are values. Return (value, error) consistently; wrap with %w for errors.Is/errors.As; never compare message strings. Handle Close errors where writes can fail. A typed nil in an error interface is not nil. Never panic on malformed external data.
  • Bound everything. No goroutine per unbounded input item — use bounded workers with admission. No unbounded retry; retries need idempotence, a shared deadline, bounded backoff. No unbounded io.ReadAll.
  • Concurrency: contexts signal cancellation, they don't kill goroutines — every worker needs a shutdown path and an owner that waits. Sender closes owned channels exactly once. One goroutine owns shared state, or guard it with a mutex/atomic protocol. Define who may mutate a pointer after it crosses a channel.
  • Resources: defer cleanup after checking acquisition succeeded; never defer in a large loop (per-item function instead). os.Exit skips deferred cleanup — return to the entry point. Never copy a used mutex.
  • Zero values and absence: document zero-value behavior. Nil slice, empty slice, nil map, missing key are distinct; use comma-ok lookups. Use *bool when omitted vs false differ.
  • Names are MixedCaps, units last: requestCount, payloadSizeBytes; durations as time.Duration internally. No underscores in ordinary identifiers; exported names start uppercase; standard initialisms (HTTPClient, userID); short receiver names.
  • Formatting: gofmt is authoritative, including its tabs. Never force spaces or a hard 100-column wrap onto gofmt output; keep lines readable through function design.
  • Functions: ordinary functions stay near or below 70 lines. Pass context.Context first for cancellable operations; never store request contexts in long-lived structs. Narrowest useful interface at the consumer boundary; options struct for complex construction.
  • State and mutation: validate, do bounded leaf work, commit at one obvious point; stale async results must not mutate a newer generation. Slices alias their backing arrays — document ownership on send/return. Config is immutable after construction.
  • Determinism: sort map keys before canonical output; never rely on goroutine completion order or int width.
  • Security: treat unsafe, cgo, reflection, plugins, and build directives as trust boundaries. Bound HTTP headers/bodies, set timeouts, verify TLS. The race detector is not proof of race-freedom.
  • Comments explain why, not what. Doc comments on exported symbols stating cancellation, concurrency, ownership, zero-value guarantees.

Output Contract

Go you produce must: build under the recorded toolchain, return (zero, err) on expected failures with wrapped sentinel errors, verify internal invariants with explicit checks, bound all input/workers/queues/ retries, pass go vet and go test -race, stay gofmt-clean with functions near ~70 lines, and pass the review checklist.

Review Checklist

  • External input validated before use; invariants checked, panic only for programmer-contract violations.
  • All bytes/items/workers/queues/retries bounded by named constants.
  • Errors wrapped with %w; no message-string comparison; Close errors handled; no typed-nil error.
  • Every goroutine has a shutdown path and a waiting owner; channel ownership and close responsibility explicit.
  • Contexts passed as first param, never stored in long-lived structs.
  • Zero-value/nil-slice/nil-map/comma-ok semantics deliberate.
  • Slice aliasing and ownership documented at boundaries.
  • Deterministic output: sorted keys, no scheduling-order reliance.
  • Secrets out of errors/logs; privileged ops (unsafe/cgo/exec) narrowly scoped.
  • Functions ≤ ~70 lines, gofmt-clean, comments explain intent.

ReferencesGo documentation · Effective Go

KotlinSafety-first Kotlin — contracts that name threading and lifecycle, sealed result types for expected failures, and budgeted resources.

Tiger Style Kotlin

Purpose

Apply the Tiger Style standard to Kotlin code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_KOTLIN.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Define the target as OS + CPU + runtime/ABI + SDK + dependencies + packaging — never just an architecture name. Check the Kotlin and Compose support tables for the exact release; neither table proves support for every dependency.
  2. Lay the module out top-to-bottom: purpose, public contract (types, entry points, threading and lifecycle expectations), then private machinery.
  3. Validate external input before allocating expensive resources or publishing state. Use assertions for internal facts, normal error paths (sealed result types) for expected external failures.
  4. Encode resource budgets as named configuration with units.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Each substantial operation identifies its accepted input, maximum work, owner, commit point, failure mode, cancellation behavior, and evidence.
  • A type-safe language is not a secure application. Null-safety does not cover authorization, deadlines, queue admission, or secret handling — those remain application responsibilities, reviewed explicitly.
  • Bound everything. No unbounded loops, retries, queues, collection growth, or coroutine fan-out. Name limits with units (input_bytes_max, retry_count, queue_capacity, deadline). Reject invalid input before expensive allocation or state publication.
  • Idioms welcome when their cost and lifetime are clear. Sealed hierarchies, extension functions, collection operations, and coroutines are fine — but do not mechanically translate Rust ownership syntax or Lua naming into Kotlin. Write Kotlin as Kotlin.
  • Coroutines need structured concurrency. Every scope has an owner and a cancellation policy; cancellation is cooperative, prompt, and cannot publish partial effects. No GlobalScope for work the app owns. Document dispatcher choices and what blocks where.
  • Lifecycle discipline (Android). No work outlives its owner: activities, fragments, and view-models release subscriptions, jobs, and listeners. Configuration changes and process death are expected events with tested behavior, not edge cases.
  • Multiplatform honesty. Android, iOS, JVM, and JS are separate lanes with separate evidence. Kotlin/Wasm and Compose web are beta — say so when used. linuxArm64 is a Tier 2 target but Linux ARM64 is not a supported host for producing final native binaries: build on a supported host, run on the board. Apple final binaries require macOS. Real-device tests for mobile lanes; simulators alone prove little.
  • Exceptions live next to the affected code: the rule, reason, affected targets, owner, remaining bound, and test or removal condition. A blanket exception for a whole application is not an engineering argument.

Output Contract

Kotlin you produce must: compile on the pinned toolchain with warnings treated as errors, carry KDoc on public items stating contract and bounds, use sealed result types (not exceptions) for expected failures, assert internal invariants, bound all work/queues/retries/ coroutines, keep lifecycle and cancellation explicit, record the target matrix evidence, and pass the review checklist.

Review Checklist

  • External input validated before expensive allocation or state publication.
  • Null-safety not mistaken for authorization, deadline, or secret-handling review.
  • Work, queues, collection growth, retries, coroutines, and time have explicit budgets.
  • Coroutine scopes owned; cancellation cooperative, prompt, and side-effect safe.
  • Android lifecycle: no leaked subscriptions, jobs, or listeners; config change and process death tested.
  • Multiplatform target recorded as OS+CPU+runtime/ABI+SDK+packaging with evidence; beta lanes labeled.
  • Errors preserve failure and cause; assertions enforce established invariants.
  • Secrets never in logs, bundles, or checked-in config.
  • Dependency and toolchain changes received execution-trust review.
  • Performance claims include measurements and their environment.

ReferencesKotlin documentation · Kotlin standard library

LuaSafety-first Lua — the runtime named up front, validation by nil-plus-error returns, no bare pcall, and every loop bounded.

Tiger Style Lua

Purpose

Apply the Tiger Style standard to Lua code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_LUA.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Determine the runtime (Lua 5.4+, LuaJIT, Neovim, embedded). Put runtime-specific behavior behind a small explicit boundary; never assume one silently.
  2. Lay the module out top-to-bottom: header/purpose, host API aliases, local M = {}, constants, LuaCATS types, validation helpers, private leaf helpers, orchestration functions, setup() if needed, return M.
  3. Validate external input with return nil, "descriptive error"; assert internal invariants with assert() (one invariant per assert).
  4. Bound everything with named *_MAX constants: loops, retries, queues, caches, histories, output sizes.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior.
  • Assertions are for programmer errors and invariants, never for external input. Never write bare pcall(do_work) — capture and propagate the error.
  • Bound everything. No unexplained while true, no unbounded retry, no unbounded growth. Retry budgets are named constants (e.g. RETRY_COUNT_MAX = 4).
  • Control flow: positive conditions, branch-light leaf loops, no ready and start() statement-shorthand, no recursion over attacker-controlled depth.
  • Nil/false discipline: never local enabled = options.enabled or true when false is valid — check == nil explicitly. Give nil one documented meaning per context.
  • Names are snake_case with units last: timeout_ms, payload_size_bytes, retry_count, generation_id. No n, sz, tmp2, stuff.
  • Formatting: 4-space indent, max 100 columns, one statement per line, trailing commas in multiline tables. stylua config: column_width = 100, indent_type = "Spaces", indent_width = 4.
  • Functions: ordinary functions stay near or below 70 lines. Prefer named option tables over positional booleans: copy_range({ source = ..., overwrite = true }).
  • State and mutation: validate input, do pure/bounded leaf work, commit at a single obvious point. Copy caller-owned config if later mutation is possible. Async callbacks carry a generation token and reject stale results.
  • Resources: one clear owner per file, socket, timer, vim.uv handle, subprocess. Teardown is idempotent and nils the handle.
  • Security: never interpolate untrusted values into shell strings — use vim.system({ "git", "status", "--", user_path }) argv form. Treat load, dofile, package.loadlib, FFI as privileged.
  • Determinism: sort map keys before serializing; never rely on pairs() order; make locale/timezone assumptions explicit.
  • Comments explain why, not what. Use LuaCATS (---@param, ---@return, ---@class) where it aids review.
  • Neovim: prefer vim.api, vim.fs, vim.system, vim.uv; keep setup() idempotent; require() loads definitions, not global side effects.

Output Contract

Lua you produce must: compile cleanly, carry LuaCATS types on exported functions, assert its invariants, return (nil, err) on expected failures, bound all loops/retries/queues, stay within 100 columns and ~70 lines per function, and pass the review checklist.

Review Checklist

  • External input validated before privileged use; internal invariants asserted.
  • All loops/retries/caches/queues bounded by named constants.
  • nil vs false semantics deliberate; no or default clobbers false.
  • Index/count/size/unit names explicit and unit-suffixed.
  • Every resource has one owner; teardown exists on failure paths.
  • No shell string interpolation of untrusted input; argv form used.
  • Async callbacks reject stale generations; handles revalidated after delay.
  • Output deterministic where persisted or tested; no pairs() order reliance.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, comments explain intent.

ReferencesLua 5.4 reference manual · LuaJIT

MojoSafety-first Mojo — pinned compiler and libraries, never a moving nightly, typed errors, and recorded evidence for every accelerator target.

Tiger Style Mojo

Purpose

Apply the Tiger Style standard to Mojo code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_MOJO.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Pin a reviewed compiler version and its matching standard library (plus MAX and accelerator libraries, pinned consistently, when used). Keep the package/environment manifest and lockfile in the project. Never use a moving nightly channel as a reproducibility claim.
  2. Record build/release evidence: compiler version, source and dependency revisions, CPU target, OS, native libraries, optimization and assertion settings, GPU model/driver/device when applicable, Python environment when Python interop is enabled.
  3. Run mojo --version, mojo build, and mojo format from the same environment Neovim uses. A global executable and a project executable can be different language releases — resolve that before weakening diagnostics or changing code.
  4. Lay the module out top-to-bottom: purpose, public contract (types, entry points, ownership), then private machinery.
  5. Validate external input with ordinary control flow returning a typed error; assert facts a correct implementation already established.
  6. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every substantial operation answers: accepted and rejected inputs, trust boundary, max work/memory/output/time, owner of every buffer/allocation/device/task, failure and cancellation behavior, cleanup obligations. A successful compilation proves none of this; nor does an impressive benchmark.
  • Ownership discipline. Mojo's ownership and borrowing are tools for the contract, not decoration. Borrow for inspection; transfer ownership when retaining or handing off is the contract. Name the owner when work escapes a scope (task, callback, device buffer, Python object).
  • Bound everything. No unbounded loops, retries, queues, or growth. Name limits with units (input_bytes_max, batch_size_max, retry_count, deadline). Long-lived services may run indefinitely, but each batch is bounded and cancellation has a documented effect.
  • Numerics are a safety property. State precision, rounding, overflow, and NaN/inf policy where they affect correctness. Keep tight numerical kernels and interactive orchestration behind explicit boundaries — they have different cost profiles.
  • Python interop is a trust boundary. Declare the Python implementation and package environment, keep the interop surface narrow and typed, and never let a Python import silently authorize device access, package installation, or network use.
  • Exceptions need a reason, a local owner, and a check. Describe what assumption would invalidate the exception. No global suppressions that make later violations invisible.
  • Toolchain upgrades are reviewed changes. Re-run formatting, compilation, tests, and representative benchmarks; keep compiler updates separate from unrelated system maintenance so failures can be attributed and rolled back. Build and test as an ordinary user; review package channels, installers, AUR build files, and foreign dependencies before executing them. Arch is rolling — never infer vendor support from local installability.

Output Contract

Mojo you produce must: compile on the pinned toolchain with diagnostics at full strength, carry doc comments on public items stating contract and bounds, return typed errors on expected failures, assert internal invariants, bound all work/queues/retries/memory, keep kernels and orchestration behind explicit boundaries, record build evidence, and pass the review checklist.

Review Checklist

  • External input validated before allocation, indexing, and mutation.
  • Every buffer, device allocation, task, and Python object has one owner and a cleanup path.
  • Work, batches, queues, memory, retries, and time have explicit budgets.
  • Numeric precision, overflow, and NaN/inf policy stated where they matter.
  • Errors preserve failure and cause; assertions enforce established invariants.
  • Python interop surface is narrow, typed, and its environment recorded.
  • Cancellation cannot publish stale results or duplicate effects silently.
  • Compiler/stdlib/MAX/accelerator versions pinned; build evidence recorded.
  • No global diagnostic suppressions; exceptions are local, owned, checked.
  • Logs, argv, and build environments expose no secrets.
  • Performance claims include measurements and their environment.

ReferencesMojo documentation · Mojo manual

NixSafety-first Nix — pure evaluation, explicit inputs, and pinned sources, validated by a pinned toolchain.

Tiger Style Nix

Purpose

Apply the Tiger Style standard to Nix expressions. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_NIX.md; this file is the operational core. When the two disagree, the full guide wins. Rules the Lua template covers but Nix lacks (async callbacks, OS handles) are omitted rather than invented.

Workflow

  1. Determine the Nix language version, evaluator/CLI version, Nixpkgs revision, module system, and target system. Declare nix-command and flakes when a project uses them; never assume them silently.
  2. Lay the module out top-to-bottom: header/purpose, pinned inputs, small let bindings with explicit names, pure expression logic first, derivations / modules / flake outputs after. Keep reusable pure logic separate from derivations and modules.
  3. Validate external input with { ok = false; error = "descriptive"; }; assert internal invariants with assert cond; value (one invariant per assert). Remember an assert only fires when that expression evaluates.
  4. Bound everything with named constants (itemCountMax, totalMax): list lengths, nesting depth, byte sizes, jobs, attempts.
  5. Review against the checklist below before calling the code done.
  6. Validate with the pinned toolchain per the guide's recipes: nixfmt --check, statix check ., deadnix --fail .; for flake projects nix flake check --no-write-lock-file once their declared experimental features are enabled.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, which derivations/builds/activation effects it selects, and failure behavior.
  • Assertions are for programmer errors and invariants, never for expected invalid configuration. For that, return a tagged { ok = false; error = ...; } or throw with context at the boundary. Force the values needed to establish a contract — shallow success proves nothing.
  • Bound everything. No unbounded fold, no recursion over attacker-controlled depth. Retry budgets are named constants and require an idempotent operation.
  • Control flow: prefer finite transforms over recursive traversal of external data. Guard a length bound before a fold, make per-element work explicit, avoid repeated list concatenation in folds.
  • Absence/null/false discipline: absence, null, false, [], and {} are different states. Never attrs.value or fallback when false or null is valid — check attrs ? value explicitly. Do not confuse module option merging with ordinary or.
  • Laziness discipline: producing an attrset is not proof its fields evaluated. Document laziness/strictness boundaries; force at deliberate boundaries with builtins.deepSeq over bounded values only. Prefer foldl'; remember it is strict only to weak head normal form.
  • Names are camelCase with units last: payloadSizeBytes, itemCountMax, totalMax. Use conventional Nixpkgs names where interoperating (pname, version, nativeBuildInputs, meta). No near-duplicate aliases of well-known attributes.
  • Formatting: 2-space indent, max 100 columns, nixfmt from the pinned dev environment (record its version/interface). Deterministic formatter output wins over manual wrapping; never force Lua's four spaces onto Nix.
  • Functions: explicit argument sets with documented required/optional fields; reject unknown arguments for narrow configuration. Ordinary functions stay near or below 70 lines. Keep policy validation separate from derivation building.
  • Scope: keep let scopes small; no broad with scopes that hide identifier origins. rec is a semantic tool, not a default. Pass the package set and system intentionally — no floating <nixpkgs> in reproducible code.
  • State: expressions do not mutate, but evaluation selects builds and activation effects. Make configuration changes as new values and review the generated plan before applying; avoid recursive attrsets when explicit dependencies suffice.
  • Security: never interpolate secrets into strings that become store paths, derivations, generated files, or logs — runtime secrets belong to a separately reviewed mechanism. Treat substituters, signing keys, and fetchers as trust boundaries; never disable the sandbox to make a build pass.
  • Shells and filesystem: buildPhase is a shell boundary — fixed commands plus lib.escapeShellArg/lib.escapeShellArgs; escape Nix interpolation separately for literal shell ${...}. A Nix path value is not a runtime-path string; interpolating a path copies content into the store, so filter sources deliberately.
  • Determinism: pin source revisions and hashes; commit a reviewed flake lock; use explicit systems. Pure evaluation is not proof of a deterministic build.
  • Comments explain why, not what. Document argument types, laziness/strictness boundaries, system assumptions, store exposure, and derivation side effects. Module options need description, example, type, and merge policy.

Output Contract

Nix you produce must: evaluate cleanly under the declared Nix version, document argument sets on functions, assert its invariants, return tagged { ok = false; error = ...; } on expected invalid input, bound all folds/traversals with named constants, stay near ≤70 lines per function and ≤100 columns, format clean with the pinned nixfmt, and pass the review checklist.

Review Checklist

  • Language/evaluator/Nixpkgs/module-system/target explicit; experimental features declared.
  • External input validated with tagged results; internal invariants asserted and forced where the contract needs it.
  • All folds/traversals bounded by named constants; no unbounded recursion over external data.
  • Absent vs null vs false vs [] vs {} deliberate; no or default clobbers a valid value.
  • Laziness/strictness boundaries documented; deepSeq used on bounded values only.
  • Names: Nixpkgs conventions where interoperating, camelCase with units for locals.
  • No secrets in store paths/derivations/logs; shell args escaped; sources filtered.
  • Dependencies pinned with hashes; flake lock committed; sandbox intact.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, nixfmt-clean, comments explain intent.

ReferencesNix manual · Nixpkgs manual · nix.dev

OdinSafety-first Odin — explicit allocators on every procedure, typed error returns, and contracts that state ownership and failure behavior.

Tiger Style Odin

Purpose

Apply the Tiger Style standard to Odin code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_ODIN.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Pin the exact compiler release and inspect its target list and help output before claiming any deployment target. A CPU architecture is not a deployment target: record OS, ABI, object format, libc, minimum SDK/runtime, native libraries, and packaging separately.
  2. Lay the package out top-to-bottom: purpose, public contract (types, entry procedures, allocator expectations), then private machinery.
  3. Make allocators explicit. Every procedure that allocates answers: which allocator, who frees, and what happens on allocator failure.
  4. Validate external input with ordinary control flow returning a typed error; assert facts a correct implementation already established.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. For every important procedure, a reviewer must be able to identify permitted inputs, maximum work, valid state, resource owner, mutation point, failure behavior, and evidence. Avoid implicit conventions that require reading the whole program to establish one invariant.
  • Odin gives control, not proofs. There are no Rust-style compile-time ownership proofs. Pointers, aliases, foreign data, manual lifetime management, and concurrent access require deliberate review. Never call code "memory-safe" merely because it compiles or keeps bounds checks enabled.
  • Bound everything. No unbounded loops, retries, queues, or growth. Name limits with units (input_bytes_max, retry_count, queue_capacity, deadline). Prefer simple data and shallow control flow; a fixed buffer, explicit loop, or dedicated arena can make an invariant clearer, but is not automatically superior — choose by the workload's limits, failure policy, and resource lifetime.
  • Allocator discipline. Default to explicit context allocators; never silently switch allocators mid-operation. Temporary arenas have documented lifetimes. Allocation failure is an expected error, not an assertion — handle it on every path that can allocate.
  • Concurrency. Document the threading model, shared-state protocol, and lock ordering. Never hold a lock across a blocking call without a stated reason and bound. Own thread/task handles; cancellation follows a documented policy; stale completions cannot publish.
  • Foreign data is untrusted. C pointers, loaded files, network buffers, and plugin inputs are validated before use: lengths, tags, versions, and provenance checked with explicit code, not assumed from the producer.
  • Deployment honesty. Linux x86-64 is the baseline native lane; every other target needs its own evidence (ARM64 libraries and board tests, MSVC/SDK compatibility for Windows, signing for Apple). Do not infer Android from Linux ARM64 or iOS from macOS ARM64. Prototype and validate the entire pipeline before committing a product to a conditional target. A phone can consume an Odin service through the same versioned API as a desktop client.
  • Exceptions record the rule, need, affected targets, owner, compensating check, and removal condition. No blanket "performance" waiver for bounds checks, allocator errors, authentication, or release testing.

Output Contract

Odin you produce must: compile on the pinned compiler with diagnostics at full strength, carry doc comments on public procedures stating contract, allocator expectations, and bounds, return typed errors on expected failures, assert internal invariants, bound all work/queues/retries/memory, make every allocation's owner and failure path explicit, record the deployment target evidence, and pass the review checklist.

Review Checklist

  • External input validated before allocation, indexing, and mutation.
  • Every allocation names its allocator, owner, free path, and failure behavior.
  • Pointers, aliases, lifetimes, and foreign data reviewed as untrusted until proven.
  • Work, queues, memory, retries, and time have explicit budgets.
  • Errors preserve failure and cause; assertions enforce established invariants.
  • Concurrency model, lock ordering, and cancellation policy documented.
  • No code called "memory-safe" without the review to back it.
  • Deployment target recorded as OS+CPU+ABI+libc+SDK+packaging with evidence.
  • Logs, argv, and subprocess environments expose no secrets.
  • Toolchain and dependency changes received execution-trust review.
  • Performance claims include measurements and their environment.

ReferencesOdin · Odin language overview

PHPSafety-first PHP — strict_types everywhere, domain exceptions for input, identity comparisons on untrusted values, and validation surviving production settings.

Tiger Style PHP

Purpose

Apply the Tiger Style standard to PHP code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_PHP.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Declare the SAPI and runtime up front (CLI vs FPM, PHP 8.3+ target, opcache/JIT settings). Never assume request isolation holds for a long-running worker — put SAPI-specific behavior behind an explicit boundary. Type declarations are dev checks, not runtime validation of untrusted input.
  2. Every file opens with declare(strict_types=1);. Lay code out top-to-bottom: namespace, imports, class/interface docblock, constants, constructor with promoted properties, public methods, private helpers. PSR-4 autoloading via Composer; including a file must not start sessions, open connections, or emit output.
  3. Validate external input by throwing a precise domain exception; assert() only for programmer invariants — remember zend.assertions=-1 strips assertions in production, so external rejection must survive it. Never use == on untrusted values; === always.
  4. Bound everything with named UPPER_SNAKE_CASE class constants: input bytes, item counts, nesting depth, retries, captured output.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior. State whether failure leaves state unchanged or partial.
  • Errors: use domain exception classes consistently per boundary; catch only failures the layer can interpret. Chain with throw new DomainException(..., previous: $e). Never use @ error suppression — a silenced failure is a hidden failure. No authorization or mutation inside assert().
  • Strict equality. == performs type juggling ("0" == false, "1e3" == 1000); it is banned at trust boundaries. Use ===/!==. empty() conflates null, false, 0, "", "0", [] — test === null explicitly when absence is the question.
  • Bound everything. No unbounded file_get_contents on remote URLs, iterator_to_array, or retry. Retries need an idempotent operation, a shared deadline, bounded backoff, and an injected jitter source.
  • Superglobals are untrusted input. $_GET, $_POST, $_COOKIE, $_SERVER, $_FILES are attacker-controlled bytes. Read them once at the boundary through a validated request object; never index them deep in business logic.
  • Serialization: never unserialize() untrusted bytes (object injection); prefer JSON with schema validation, or pass ['allowed_classes' => false]. Never eval() external text.
  • Database: PDO with real prepared statements (ATTR_EMULATE_PREPARES => false); never interpolate values into SQL.
  • Shell: every proc_open/exec call gets an explicit argv array, cwd, env, deadline, and output bounds; escape with escapeshellarg(). Distinguish exit status, signal, timeout, and launch failure.
  • Includes: never include/require a path built from user input (LFI). Autoload via Composer PSR-4; allowlist any dynamic class resolution.
  • State: no mutable globals or static caches holding request data in long-running processes (workers, RoadRunner, FrankenPHP); the shared-nothing request model does not hold there. Inject dependencies; construct per request or prove reuse is safe.
  • Concurrency: PHP is shared-nothing per request; Fiber (8.1+) is cooperative, not parallel. Bound fiber counts; never assume ordering across fibers without explicit synchronization.
  • Names: camelCase methods/variables, PascalCase classes, UPPER_SNAKE_CASE constants, units in names ($payloadSizeBytes). Typed properties everywhere; readonly where mutation is not part of the contract. No mixed to silence diagnostics.
  • Formatting: 4-space indent, 120 columns, one configured formatter — php-cs-fixer with PSR-12 (or Laravel Pint). Stable import ordering, no competing formatters in sequence. Pin formatter and analyzer versions.
  • Functions: ordinary methods stay near or below 70 lines. Named arguments for policy flags instead of positional booleans. Separate decoding, validation, computation, and I/O; return a stable shape for similar errors.
  • Resources: one clear owner per handle, lock, transaction, process. try/finally for cleanup; register cleanup as acquisition succeeds. Never rely on destructors for timely release.
  • Determinism: sort canonical keys before serializing; never let array-order or hash iteration define a wire format. Inject clocks and randomness; random_bytes/random_int for tokens.
  • Comments explain why, not what. Docblocks carry the public contract: units, accepted types, thrown exceptions, side effects.

Output Contract

PHP you produce must: open with declare(strict_types=1);, type every property/parameter/return, throw precise domain exceptions on expected failures, assert only internal invariants (valid with zend.assertions=-1), use === at trust boundaries, bound all input/loops/retries/output with named constants, read superglobals once at the boundary, use real prepared statements, stay within 120 columns and ~70 lines per method, and pass the review checklist.

Review Checklist

  • declare(strict_types=1); present; all types declared, no mixed escapes.
  • External input validated by explicit throw; invariants asserted (and valid with assertions disabled).
  • === at every trust boundary; no ==, no empty() for absence checks.
  • All input sizes, loops, retries, output captures bounded by named constants.
  • null vs false/0/"" semantics deliberate.
  • Superglobals read once at the boundary into a validated object.
  • No unserialize/eval on untrusted data; no user-controlled include paths.
  • PDO real prepares; shell calls use argv form with timeout and cleanup.
  • Every resource has one owner; cleanup runs on failure paths.
  • Deterministic output where persisted or tested.
  • Methods ≤ ~70 lines, lines ≤ 120 cols, one formatter (php-cs-fixer PSR-12 or Pint).

ReferencesPHP manual · PSR-12 coding style

PythonSafety-first Python — typed signatures, precise domain exceptions, bounded I/O and retries, and validation that survives python -O.

Tiger Style Python

Purpose

Apply the Tiger Style standard to Python code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_PYTHON.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Declare the interpreter (CPython 3.12+ target) and platform up front. Never assume the GIL, reference counting, or a particular ABI — put runtime-specific behavior (3.14 free-threading, OS adapters) behind an explicit boundary. Type hints are dev checks, not runtime validation.
  2. Lay the module out top-to-bottom: docstring/purpose, public types and constants, private validation helpers, leaf logic, orchestration, explicit main() with if __name__ == "__main__":. Keep __init__.py light; imports must not start threads or touch services.
  3. Validate external input by raising a precise domain exception (e.g. ValidationError); assert only programmer invariants — remember python -O strips assert, so external rejection must survive it.
  4. Bound everything with named UPPER_SNAKE_CASE constants: input bytes, item counts, nesting depth, retries, queues, captured output.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior. State whether failure leaves state unchanged or partial.
  • Errors: use domain exception classes (or one explicit result type) consistently per boundary; catch only failures the layer can interpret. Add context with raise DomainError(...) from exc. Never except BaseException: pass — interrupts and cancellation must propagate. No authorization or mutation inside assert.
  • Bound everything. No unbounded list(iterator), task creation, or retry. Retries need an idempotent operation, a shared deadline, bounded backoff, and an injected jitter source.
  • Control flow: early returns for invalid cases, ordinary loops for stateful work. Comprehensions only for simple transforms of already bounded collections; no nested comprehensions with hidden effects.
  • Absence discipline: value is None, never value or default when False/0 is valid. Use a sentinel to distinguish omitted from explicit None; never use mutable default arguments.
  • Names: snake_case functions/variables, PascalCase classes, UPPER_SNAKE_CASE constants, units in names (payload_size_bytes, deadline_monotonic_s). Leading underscore for nonpublic; never invent dunder methods.
  • Type hints: annotate public functions; prefer Protocol over broad object. Hints describe real values — no Any/cast to silence diagnostics, and never an unchecked cast after an annotation. Decide deliberately whether bool counts as int (prefer exact type(x) is int at strict boundaries).
  • Formatting: 4-space indent, 100 columns, one configured formatter — Ruff's ([tool.ruff] target-version = "py312", line-length = 100). Stable import ordering, no competing formatters in sequence. Pin formatter and type-checker versions.
  • Functions: ordinary functions stay near or below 70 lines. Use keyword-only arguments for policy flags instead of positional booleans. Separate decoding, validation, computation, and I/O; return a stable shape for similar errors.
  • Resources: one clear owner per file, lock, transaction, subprocess. Use context managers (ExitStack/AsyncExitStack for variable counts); register cleanup as acquisition succeeds. Never rely on __del__; never return truthy from __exit__ by accident.
  • Security: never pickle.loads untrusted bytes, eval/exec external text, or import from untrusted paths. Parameterized SQL, argv arrays with shell=False, validated URL schemes. A venv is not a sandbox.
  • Processes: every subprocess call gets explicit argv, cwd, env, deadline, and output bounds; a timeout has a cleanup owner that stops, drains, and reaps. Distinguish exit status, signal, timeout, and launch failure.
  • Concurrency: asyncio.TaskGroup for related tasks; bound admission before creating tasks. Re-check state after every await; clean up in finally and re-raise CancelledError. Use processes for killable untrusted computation.
  • Determinism: sort canonical keys before serializing; never let set iteration order define a wire format. Inject clocks and randomness; use secrets for tokens, monotonic clocks for deadlines.
  • Comments explain why, not what. Docstrings carry the public contract: units, accepted types, raised exceptions, side effects, cancellation behavior.

Output Contract

Python you produce must: run under python and python -O identically, carry type hints on public functions, raise precise domain exceptions on expected failures, assert only internal invariants, bound all input/loops/retries/queues with named constants, close every resource via a context manager, stay within 100 columns and ~70 lines per function, and pass the review checklist.

Review Checklist

  • External input validated by explicit raise; invariants asserted (and valid under -O).
  • All input sizes, loops, retries, queues, output captures bounded by named constants.
  • None vs False/0 semantics deliberate; no mutable default args.
  • Names explicit with units; type hints real, no Any/cast escapes.
  • Every resource has one owner; cleanup runs on failure paths.
  • No pickle/eval of untrusted data; subprocess uses argv form with timeout and cleanup owner.
  • Async code re-checks state after await; cancellation propagates.
  • Output deterministic where persisted or tested; no set-order reliance.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, one formatter (Ruff).

ReferencesPython documentation · PEP 8

RustSafety-first Rust — pinned toolchain, typed errors over panics, an owner for every allocation and task, and bounds with units.

Tiger Style Rust

Purpose

Apply the Tiger Style standard to Rust code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_RUST.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Pin the toolchain in the project's rust-toolchain.toml (an exact, reviewed nightly date), not only in editor config. Record the active toolchain when terminal, editor, and CI disagree.
  2. Lay the crate out top-to-bottom: crate docs/purpose, public contract (types, constructors, key functions), then private machinery. Private first, then pub(crate), then pub only for a real external API.
  3. Validate external input with ordinary control flow returning a typed error; assert facts a correct implementation already established (assert! for invariants, debug_assert! for expensive dev checks).
  4. Bound everything with named constants carrying units: input bytes, iterations, retries, queue capacity, memory, output, deadlines.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every substantial operation answers: accepted and rejected inputs, trust boundary, max work/memory/output/time, owner of every allocation/handle/task/subprocess, failure and cancellation behavior, cleanup obligations.
  • Errors vs assertions. Invalid input/config/protocol data → typed error with bounded context. Corrupt internal relationship → assert!. Never unwrap()/expect() on external data, I/O, locks, or subprocess results. Never put required mutation inside an assertion. Propagate with ? where the caller owns recovery; match explicitly where failure changes cleanup, retry, or state. Never let _ = a Result unless the loss is recorded or justified as best-effort.
  • Bound everything. No unbounded loops, retries, queues, or growth. Name limits with units (output_bytes_max, retry_count, queue_capacity, deadline). Validate index ranges with checked arithmetic (checked_sub/checked_add) before computing an end. Use fixed-width integers for wire/disk formats; usize for native indexing only after checked conversion (TryFrom, never unchecked as on external sizes).
  • Ownership discipline. Borrow for inspection (&str, &[T]); take ownership when retaining or transferring is the contract. Treat .clone() as an explicit cost decision — cloning an Arc differs from cloning a buffer. RAII for ordinary resources; name the owner when work escapes a scope (task, callback, Arc, subprocess).
  • Unsafe policy. Default to #![forbid(unsafe_code)]. Where unsafe is genuinely required: isolate the boundary, document every safety obligation locally (allocation, provenance, alignment, init, bounds, aliasing, lifetime, thread access), consider #![deny(unsafe_op_in_unsafe_fn)], and add adversarial tests to the safe wrapper. Never unwind across an FFI boundary.
  • Concurrency. Bounded queues with a documented full-queue policy (reject, block-to-deadline, replace-obsolete). Document lock ordering and the protected invariant; never hold a blocking mutex across .await. Own task handles; cancellation follows a documented policy. Use generation tokens so stale completions cannot publish.
  • Control flow: early returns, shallow branches, no recursion over attacker-controlled depth (explicit stack with a depth cap instead). Retries need a retryable error class, bounded attempts, a total deadline, and an idempotency argument.
  • Names carry domain meaning and units: snake_case for items/locals, UpperCamelCase for types/traits, SCREAMING_SNAKE_CASE for constants. No n, sz, tmp2, stuff. Prefer options structs over positional booleans.
  • Formatting: rustfmt is the mechanical authority — 4-space indent, 100 columns, edition 2024. Ordinary functions stay near or below 70 physical lines; split at meaningful contracts. Explicit imports; no wildcard imports in production modules.
  • Types and state: distinct types for distinct concepts (byte offset vs element count, validated vs raw config). Enums for mutually exclusive states. State transitions follow validate → prepare → commit → observe; rejected validation leaves published state unchanged.
  • OS boundaries. Paths are requests, not authorization — no string prefix checks for containment. std::process::Command with separate argv values (no implicit shell); bound and drain stdout/stderr, enforce deadlines, reap children. Atomic writes via temp-file + rename. Never log tokens or credential-bearing URLs; no secrets in argv.
  • Determinism: sort map keys before serializing; never depend on hash-map iteration order; make float-tolerance decisions explicit with units and a reason.
  • Comments explain why, not what. Keep rustdoc on public items focused on contract, bounds, and failure behavior.
  • Neovim: use the project's pinned toolchain for rust-analyzer, checks, formatting, and debugging alike. Helpers feeding diagnostics use a versioned schema with bounded records, explicit byte/char and indexing conventions, and buffer identity + input version to reject stale results.

Output Contract

Rust you produce must: compile on the pinned nightly with warnings denied, carry doc comments on public items stating contract and bounds, return typed errors on expected failures, assert internal invariants, bound all work/queues/retries/memory, stay within 100 columns and ~70 lines per function, and pass the review checklist.

Review Checklist

  • External input validated before allocation, indexing, and mutation.
  • Types distinguish domain concepts; invalid states hard to construct.
  • Work, queues, memory, output, retries, and time have explicit budgets.
  • Arithmetic and conversions preserve sizes and protocol meanings.
  • Errors preserve failure and cause; assertions enforce established invariants.
  • Every resource (allocation, handle, task, subprocess) has one owner and a cleanup path.
  • Cancellation cannot publish stale results or duplicate effects silently.
  • Unsafe/FFI contracts are local, reviewed, and adversarially tested where possible.
  • Logs, argv, and subprocess environments expose no secrets.
  • Toolchain and dependency changes received execution-trust review.
  • Boundary tests and the actual supported target/feature matrix are recorded.
  • Performance claims include measurements and their environment.

ReferencesThe Rust Book · Clippy documentation · Rust API Guidelines · TigerStyle

ScalaSafety-first Scala — total functions over ADTs, input parsed into validated domain values, effect discipline, and bounded executors and queues.

Tiger Style Scala

Purpose

Apply the Tiger Style standard to Scala 3 code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_SCALA.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Declare the platform up front (Scala 3.x, JDK, build tool, effect library). Keep runtime-specific behavior behind a small explicit boundary; never assume one silently. No experimental compiler features without an explicit policy and migration tests.
  2. Lay the module out top-to-bottom: package, opaque types / enums / ADTs, validation, pure operations, wiring. Group a small domain's types, validation, and pure operations together.
  3. Parse external input into validated domain values with Either/Option; assert internal invariants with assert, programmer preconditions with require. Never put validation or side effects only inside them — the compiler can elide them.
  4. Bound everything with named constants (ItemCountMax = 1024): input bytes, collection sizes, nesting depth, queues, executors, retries, captured output.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every public operation answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior — and whether failure leaves state unchanged, partially updated, or requiring recovery.
  • Errors are values. Either[DomainError, A] for expected rejection, Option[A] for absence, Try at boundaries where exception-producing code must become a value. Never .get as the ordinary path. Model errors as enums/ADTs — callers never parse exception text. Catch NonFatal only when recovery is meaningful; never catch all Throwable and return a success-shaped default.
  • Bound everything. No unbounded loops, retries, queues, or growth. Retries need an idempotent operation, a shared deadline, bounded backoff, and named constants.
  • Control flow: exhaustive matches on enums and sealed hierarchies — a new case is a review event, and a catch-all may conceal it. Prefer direct named steps to dense operator chains. @tailrec where tail recursion is required; still bound total work. Collection pipelines must have known input/output budgets.
  • Option/null discipline: Option for optional domain values, never sentinel strings or null. Convert nullable Java values at one boundary with a deliberate policy (Some(null) and Option(null) differ). Distinguish missing configuration from a present false or zero.
  • Names are lowerCamelCase values/methods, UpperCamelCase types, units last: timeoutMs, payloadSizeBytes, retryCount. Opaque types named by meaning (PayloadBytes). No n, sz, tmp2, stuff, and no symbolic operators whose failure behavior isn't obvious.
  • Formatting: Scalafmt with an exact pinned version and Scala 3 dialect, 2-space indent, max 100 columns. Never damage formatter output to satisfy a metric.
  • Functions: explicit result types on public methods, named parameters for policy, ordinary functions near or below 70 lines. Separate parsing, validation, computation, and effects. Keep givens/contextual parameters local and unsurprising.
  • State and mutation: pure transitions separate from effects; immutable value plus one controlled commit point, or explicitly synchronized mutable state. val is a stable binding, not deep immutability. Async refreshes carry generation checks and reject stale results.
  • Resources and concurrency: one clear owner per resource; Using for synchronous AutoCloseable — never leak a lazy value or running Future past it. Future is eager and Await timeouts don't cancel; executor ownership names who stops admission, cancels work, and awaits termination. One coherent effect model per subsystem.
  • Security: argv form (ProcessBuilder with separate args), never shell-string interpolation. Reflection, macros, Java serialization, and dynamic class loading are privileged. Parameterize SQL; type safety doesn't sanitize embedded languages.
  • Determinism: sort keys where stable order is required; inject clocks, random sources, and schedulers at test boundaries; collect parallel work by explicit sequence when output is ordered.
  • Comments explain why, not what. Scaladoc on public types/methods documents algebraic error cases, resource scope, execution-context ownership, and cancellation semantics.

Output Contract

Scala you produce must: compile cleanly, carry Scaladoc on public API, assert its invariants, return Either/Option on expected failures, bound all work with named constants, stay within 100 columns and ~70 lines per function, and pass the review checklist.

Review Checklist

  • External input validated before privileged use; internal invariants asserted.
  • All loops/retries/queues/executors bounded by named constants.
  • Option vs null vs false semantics deliberate; Java nulls converted at one boundary.
  • No .get on Option/Either/Try as the ordinary path; errors modeled as ADTs.
  • Matches on sealed ADTs exhaustive; no concealing catch-all.
  • Names carry units last; no cryptic temporaries or opaque operators.
  • Every resource has one owner; teardown exists on failure paths; no lazy leak past Using.
  • Cancellation stops or joins owned work; Await timeout not mistaken for cleanup.
  • Async refreshes reject stale generations; executor shutdown owned.
  • No shell string interpolation of untrusted input; argv form used.
  • Output deterministic where persisted or tested; keys sorted, clocks injected.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, comments explain intent.

ReferencesScala documentation · Scala 3 Book

TypeScriptSafety-first TypeScript under the strict profile — explicit contracts, strict null handling, and bounded async work.

Tiger Style TypeScript

Purpose

Apply the Tiger Style standard to TypeScript code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_TYPESCRIPT.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Pin the toolchain: TypeScript version (5.9+), strict profile (strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitOverride, noFallthroughCasesInSwitch, noEmitOnError), module format (ESM vs CommonJS), target, runtime (Node vs browser).
  2. Lay the module out top-to-bottom: types/interfaces, constants, runtime validators, pure functions, effectful adapters. Never start timers, sockets, or downloads on import.
  3. Validate external input at the boundary with a discriminated Result<T, E> (or the subsystem's declared contract); assert internal invariants with real runtime guards. Types are erased — no type can stand in for validation.
  4. Bound everything with named *_MAX constants: loops, retries, queues, caches, promise admission, output sizes.
  5. Review against the checklist below before calling the code done.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior. Public functions carry explicit return types.
  • Static/runtime gap. as, satisfies, interfaces, branding, and readonly guide trusted code only. A branded type needs a validated constructor; satisfies does not validate JSON. Audit both sides.
  • Assertions test the condition. User-defined assertion signatures must actually narrow unknown. Never use ! or casts to bypass external validation; console.assert does not terminate execution.
  • Bound everything. No unbounded Promise.all, no unbounded retry, no unbounded cache. Retries need idempotency, a shared deadline, bounded backoff, and injected jitter.
  • Control flow: discriminated unions with exhaustive switch (a never check only counts after the runtime boundary validated the discriminator). No nested ternaries for effectful logic. No recursion over attacker-controlled depth.
  • Absence discipline: undefined, null, false, 0, "" are different. Use ?? only when null and undefined both mean missing — never || for defaults when false/0 is valid.
  • Numbers: number is floating-point. Use Number.isSafeInteger, reject NaN/infinity and out-of-range values; bigint needs explicit serialization (JSON can't serialize it).
  • Names are camelCase with units in the name: timeoutMs, payloadSizeBytes, retryCount. Types/classes are PascalCase. No I prefixes on interfaces, no single-letter generics with domain roles.
  • Formatting: 2-space indent, max 100 columns, one statement per line. One pinned formatter and lint profile (e.g. prettier + eslint).
  • Functions: ordinary functions stay near or below 70 lines. Prefer discriminated option objects over positional booleans.
  • State and mutation: compute proposed state, validate, commit at one obvious point. const prevents rebinding, not object mutation. Async callbacks carry a generation/request identity and reject stale results.
  • Resources: one owner per timer, listener, file handle, socket, subprocess, worker. try/finally with explicit dispose/close; clear timers, remove listeners, abort owned I/O. FinalizationRegistry is not cleanup.
  • Async: pass AbortSignal where supported; Promise.race with a timeout is not cancellation. Keep a bounded task registry, abort owned work on teardown, await settlement.
  • Security: never eval/new Function on untrusted data; no innerHTML for text. The type checker is not a sandbox. Never interpolate untrusted values into shell strings — use spawn/execFile with argv and shell: false.
  • Filesystem: path normalization is not authorization; startsWith(root) is not containment. Write temp file in destination dir, check errors, then rename; restrictive permissions for sensitive files.
  • Determinism: explicit serialization order; inject clock/random in tests; async completion order never determines persisted results.
  • Comments explain why, not what. TSDoc on public APIs: limits, units, mutation, errors, cancellation. Explain every deliberate any, !, or cast at its narrow boundary.
  • Dependencies: committed lockfile, frozen/immutable CI installs; review install scripts, native addons, and build plugins.

Output Contract

TypeScript you produce must: compile under the strict profile with tsc --noEmit, carry explicit types on exported functions, assert its invariants with real runtime guards, return Result<T, E> (or the declared contract) on expected failures, bound all loops/retries/queues/promise admission, stay within 100 columns and ~70 lines per function, and pass the review checklist.

Review Checklist

  • External input validated before trusted use; internal invariants guarded.
  • No as/!/branding used to bypass validation; static/runtime gap audited.
  • All loops/retries/caches/queues/promise sets bounded by named constants.
  • undefined/null/false/0 semantics deliberate; no || default clobbers.
  • Numeric domains validated (isSafeInteger, NaN/infinity rejected); units in names.
  • Every resource has one owner; teardown exists on all paths incl. cancellation.
  • Async work abortable; stale results rejected; timeouts own cleanup.
  • No shell string interpolation; argv form with shell: false.
  • Output deterministic where persisted or tested; no enumeration-order reliance.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, comments explain intent and any any.

ReferencesTypeScript documentation · Biome

WGSLSafety-first WGSL — explicit shader contracts, 16-byte uniform alignment, naga validation, and portability proven on every target backend.

Tiger Style WGSL

Purpose

Apply the Tiger Style standard to WGSL shaders. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_WGSL.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Define the shader's contract: inputs (uniforms, textures, vertex attributes), backends targeted, maximum per-pixel work, fallback on shader-creation failure.
  2. Write the WGSL with 16-byte uniform alignment (every vec3 pads to 16 bytes; prefer vec4). Document bind group layout in a comment.
  3. Validate with naga (strip Bevy #import lines first — they are not WGSL): grep -v "^#import" shader.wgsl > /tmp/check.wgsl && naga /tmp/check.wgsl
  4. Check wgsl-analyzer diagnostics (ignore #import line false positives for Bevy shaders; use wgsl_bevy filetype).
  5. Run the portability test (tests/shader_render.rs) and capture a screenshot on target hardware. Naga cannot catch visual bugs.
  6. Review against the checklist below before calling the shader done.

Operating Rules

  • Contracts first. Every shader answers: accepted/rejected inputs, trust boundary (uniform values from CPU), max ALU/texture work per pixel, owner of every binding slot, failure behavior (fallback material), backend coverage.
  • Portability is safety. GLES3 (Android) is the constraint driver. No f64, no @compute without backend testing, no dynamic indexing, no subgroup ops without extensions. The portability test enforces this; do not weaken it without a backend-specific validation plan.
  • Alignment discipline. Uniform structs must be 16-byte aligned. Order fields largest-first or add explicit _pad fields. Verify the Rust ShaderType struct matches field-for-field (order and types). structLayoutHints = true in the editor shows offsets visually.
  • Bind groups are engine contracts. Bevy Material2d = group 2, UiMaterial = group 1. Document the layout in a shader comment. If the engine changes it, the comment makes the breakage obvious.
  • Bevy #import is not WGSL. Strip before naga. Do not "fix" wgsl-analyzer errors on #import lines. Use wgsl_bevy filetype for Bevy shaders (analyzer stays quiet, naga CLI validates).
  • Bound everything. Named constants with units (MAX_PULSE_WIDTH, MIN_SOFTNESS). Guard divisions (max(w, 1e-4)). Loop bounds statically determinable or constant-bounded.
  • No float equality. Use epsilon comparisons. Do not rely on NaN bit patterns or denormal handling across backends.
  • Uniformity. Texture samples need uniform control flow. Prefer procedural shaders (no textures) for 2D effects. Restructure instead of suppressing derivative_uniformity unless safety is proven.
  • Errors vs degradation. Shader-creation failure → log, fall back to safe material, never black screen silently. Define the fallback before writing the shader.
  • Test gates. naga validation + portability test + wgsl-analyzer
    • screenshot on hardware. All four before done. Headless tests use Option<ResMut<Assets<T>>> so missing material plugins don't panic.

Review Checklist

  • naga validates (imports stripped)
  • Uniform structs 16-byte aligned (Rust side matches)
  • Bind group indices correct for material type
  • No non-portable constructs (f64, dynamic indexing, etc.)
  • requires directives document the feature bar
  • Fallback behavior defined
  • Screenshot on target hardware
  • tests/shader_render.rs passes

ReferencesWGSL specification · Bevy

ZigSafety-first Zig — explicit allocators, documented error unions, comptime proofs, and tests run in Debug and ReleaseSafe alike.

Tiger Style Zig

Purpose

Apply the Tiger Style standard to Zig code. Priority order, always: Safety > Performance > Developer Experience. The full guide lives in references/TIGER_STYLE_ZIG.md; this file is the operational core. When the two disagree, the full guide wins.

Workflow

  1. Pin the toolchain (Zig 0.16.0, target triple, libc, optimization mode). Copy signatures from that version's standard library; never transplant older std.fs or child-process snippets across releases.
  2. Lay the module out top-to-bottom: imports, domain constants, public types/error sets, validation, private leaf work, public orchestration, tests. Export only the intended API.
  3. Validate external input with error unions; assert internal invariants with std.debug.assert (one invariant per assert); prove static properties with comptime checks.
  4. Bound everything with named *_max constants: loops, allocations, retries, queues, nesting depth, captured output.
  5. Validate before calling it done: zig fmt --check, zig test in Debug, then zig test -O ReleaseSafe and zig test -O ReleaseFast so rejection survives disabled safety checks.

Operating Rules

  • Contracts first. Every function answers: permitted inputs, rejected inputs, max work, what state changes, who owns each resource, failure behavior — including whether failure leaves state unchanged or partial.
  • Errors are values. Return documented error unions (e.g. ValidationError!u64); try to propagate, catch only for a specific recovery policy. No generic anyerror on public surfaces. defer is unconditional scope cleanup; errdefer is error-path cleanup until ownership transfers — never both freeing the same allocation.
  • unreachable is a correctness claim, never a fallback. Never route malformed input, OOM, timeouts, or user configuration to catch unreachable; it may become unchecked UB in unsafe builds.
  • Ownership is explicit. defer immediately after acquisition; errdefer while constructing an owned result, then transfer ownership on success. Document which allocator must free each returned allocation. Slices borrow, they do not own — never return a stack slice. One owner per mutable object.
  • Allocators are parameters, not globals. Prefer caller-provided buffers or fixed capacities when the maximum is known; otherwise accept an allocator, cap sizes, and test allocation failure. Never mix allocators across free/resize; use std.testing.allocator in tests. An arena is not a memory bound.
  • Bound everything. No unbounded recursion (explicit stacks/depth budgets), no unbounded retry, no unbounded growth. Retries need idempotency, a shared deadline, and bounded backoff.
  • Numbers: widths by domain; usize for addressable lengths, never as a wire format. @addWithOverflow/@mulWithOverflow when explicit reporting is needed; +%/+| only when that behavior is the specification. Validate narrowing and float-to-int domains; encode endianness explicitly.
  • Absence vs failure: ?T is absence, E!T is failure. Unwrap with if/orelse at boundaries; .? only after an established invariant. undefined is not a default — every read follows a write.
  • Control flow: exhaustive switch over tagged unions for domain states; no broad else that hides a new state. Policy decisions outside hot loops.
  • Names: TitleCase types, camelCase functions, snake_case values/fields, units last (timeout_ms, payload_size_bytes, item_count_max). No n, sz, tmp2.
  • Formatting: zig fmt owns formatting — four-space layout, ~100 columns, ~70-line functions, one statement per line. zig fmt --check in CI; never manually fight its stable output.
  • Concurrency: pass the I/O dependency explicitly (0.16's interface-based I/O); bound group size, cancel and await owned work before freeing its data, and treat cancellation as a real error path. A concurrency-safe allocator does not make the objects it allocates thread-safe.
  • Security: FFI, pointer casts, int-to-pointer, dynamic libraries, build scripts, and child processes are privileged boundaries. Localize casts with alignment/provenance/length/lifetime justification. Child processes use argv form — never hide a shell in a helper that claims a safe argv list.
  • Determinism: sort keys before serializing; never serialize raw struct memory or hash-map iteration order; inject clocks and random sources in tests.
  • Comments explain why, not what — especially why an unchecked operation is valid at that point. //! for modules, /// for public declarations.

Output Contract

Zig you produce must: compile cleanly under the pinned toolchain, pass zig fmt --check, document allocator ownership and error sets on exported functions, return error unions on fallible paths, assert invariants, bound all loops/allocations/retries, stay near 70 lines per function and 100 columns per line, and pass the review checklist.

Review Checklist

  • External input validated via error unions in every build mode; internal invariants asserted; no catch unreachable on external failure.
  • All loops/allocations/retries/queues bounded by named constants.
  • ?T vs E!T semantics deliberate; no .? without an established invariant; no undefined reads.
  • Every allocation has one owner; defer/errdefer placed correctly; the freeing allocator is documented.
  • Public error sets are meaningful; no anyerror leakage.
  • Integer widths and overflow policy documented; narrowing validated.
  • No hidden global allocator; no mixed allocators; no unjustified FFI casts; child processes use argv form.
  • Cancellation handled; no use-after-free of owned work; shared thread state and synchronization explicit.
  • Output deterministic where persisted or tested; no hash-map order reliance.
  • Functions ≤ ~70 lines, lines ≤ 100 cols, zig fmt clean, comments explain why.

ReferencesZig learning resources · Zig language reference · TigerStyle

These orders are standing: they do not expire between releases, and they are not suspended for deadlines. Every project on the solutions page was built under them — that is what the phrase means when it appears in a repository: the work was done under these orders.