Skip to content

Latest commit

 

History

History
1265 lines (1153 loc) · 103 KB

CHECKS.md

File metadata and controls

1265 lines (1153 loc) · 103 KB

Checks

  • Generated: 2026-11-07
  • Checks: 83
  • Tools: 48
  • Shell-script checks: 10
  • Checks with a repair pass: 22
  • Rules: 369 custom local rules, or non-default strict linter settings
  • Meta rules: 20 of those rules are rules about rules

Every check is listed across every MISE_ENV, filed under the tool it runs. Each entry names the env whose config declares it, the aggregate that runs it as part of mise run check, the fix or fmt pass that repairs what it reports, where one does, and the config files it reads. mise run fix runs every such pass. Generated by mise run gen:checks from the task tables in .mise/config*.toml, the rule files under config/ and the [workspace.lints] in Cargo.toml. It is not held to them on every change: mise run checks-md-check fails only once the generation date above is more than 7 days old.

This repository even has rules about rules. A meta rule, marked (meta) below, checks the checks rather than the code: that a rule held to every file carries no exemption, in its config or in a suppression comment; that every policy is tested; that a rule's path filters say what they mean; that the duplication baseline only shrinks. Without them a check could be weakened quietly and still pass. A YAML rule is one when its metadata sets meta: true, and a Rego policy, with every rule in it, when its METADATA annotation does.

action-validator

  • action-validator-check (default env; run by check:rust) -- Validate GitHub Actions workflow + composite-action YAML.

actionlint

  • actionlint-check (default env; run by check:rust) -- Lint GitHub Actions workflows (actionlint): local-action input validation, expression syntax, shellcheck.

agent-lint

  • agent-lint-check (default env; run by check:rust) -- Lint this repository's agent configuration (AGENTS.md, .claude/) with agent-lint.

ast-grep

  • ast-grep-check (default env; run by check:rust) -- Run ast-grep structural-search rules. Repaired by ast-grep-fix. Config: config/ast-grep/sgconfig.yaml.
  • ast-grep-check-rule-tests (default env; run by check:rust) -- Run the ast-grep rule tests against their valid and invalid cases (ast-grep test). Config: config/ast-grep/sgconfig.yaml.

Rules, from config/ast-grep/rules:

  • command-requires-command-error -- A file that imports std::process::Command via use must also use command_error::CommandExt (import it as use command_error::CommandExt as _;) so command execution goes through the checked methods (status_checked / output_checked / spawn_checked), whose errors carry the command line and exit status -- a raw std Command's .status() / .output() / .spawn() errors carry none of that. If a call genuinely needs the raw outcome (e.g. a test that asserts a non-zero exit, or an exit-code proxy), fully-qualify std::process::Command at the call site instead of importing it, so this rule does not apply.
  • const-at-top -- Declare module-level const and static items at the top of the file, after the use lines and before any type, trait, impl or function. A reader then meets the module's fixed values before the code that uses them.
  • derive-attribute-order -- Put #[derive(...)] before every inert or helper attribute on the same item -- #[non_exhaustive], #[repr], #[cfg_attr], and derive helpers such as #[serde], #[command], #[strum] or #[asyncapi]. Only doc comments, #[cfg], #[expect] and attribute macros that must see the item before the derive runs (#[serde_inline_default], #[serde_as], #[wasm_bindgen]) may come first. One order everywhere puts an item's derive list in the same place on every type, and keeps each helper next to the derive that reads it. A new attribute macro that has to precede the derive joins the allow-list in this rule.
  • doc-summary-ends-with-period -- A doc comment's first line must be a complete one-line summary ending in terminal punctuation (., !, or ?). Put any elaboration in a following paragraph after a blank /// line so the summary stays a single line -- rustdoc and readers treat that first line as the item's summary.
  • gha-check-lane-fetch-depth-0 -- The check lane's actions/checkout@... step must declare fetch-depth: 0 in its with: block. Two of the checks this lane runs read the commit graph rather than the worktree. At depth 1 the only commit that exists is the tip, so every commit hash written into a tracked file resolves to nothing and the lane fails on a repo that is perfectly fine -- while the branch-freshness check, having no origin/main to compare against, quietly skips itself and reports success. The history is a few MiB, so fetching all of it costs close to nothing here.
  • gha-checkout-fetch-depth-1 -- Every actions/checkout@... step in these workflows must declare fetch-depth: 1 in its with: block. Explicit-beats-implicit (the action default is already 1, but pinning it at the call site keeps the value visible in review and ensures a future default change doesn't silently start cloning more), and the rule's primary purpose is to prevent fetch-depth: 0 from creeping back in -- nothing these lanes run reads the commit graph, so cloning every commit just burns runner minutes and disk. The check lane is excluded here, and held to the opposite requirement, because two of the checks it runs do read the commit graph and resolve nothing at depth 1.
  • gha-default-shell-bash -- Every GitHub Actions workflow must declare the default run shell as bash with -euo pipefail, exactly: defaults: run: shell: bash --noprofile --norc -euo pipefail {0} so steps run in bash on every runner (Windows included) with strict-mode flags on every step -- no per-step set -euo pipefail boilerplate needed. GHA's bare shell: bash already implies -e -o pipefail; this explicit form additionally adds -u (unset variable -> error). (Pairs with gha-no-step-shell, which forbids per-step shell:.)
  • gha-job-timeout-minutes -- Every GitHub Actions job must declare timeout-minutes: at the job level. The runner's default per-job timeout is 360 minutes (6 hours), which is long enough that a hung job (deadlocked test, infinite-recurse cargo resolver, stuck docker pull) drains the runner-minutes budget before anyone notices. Pin every job to a realistic ceiling for that job's work. Use ${{ matrix.timeout }} or similar if the right value varies by entry.
  • gha-no-folded-strip -- Don't use >- (folded block scalar with strip-chomping) in workflow YAML. The output reflows whitespace and silently swallows trailing newlines, which bites when the step's run: is a shell command that depends on line breaks -- a >- block joins lines with spaces, not newlines. Use either: - a plain single-line run: <command>, or - a literal block run: | (which preserves newlines verbatim).
  • gha-no-step-shell -- Don't set shell: on a workflow step. Every workflow defaults to bash (defaults.run.shell) and steps must use it -- write the step in bash (e.g. sc/net/printf instead of PowerShell cmdlets, even on Windows runners, which have Git Bash) rather than overriding the shell per step. No per-step shell override is permitted in these workflows.
  • gha-no-step-shell-upstream-cache -- In upstream-cache.yaml the only permitted per-step shell: override is shell: msys2 {0} -- the wrapper msys2/setup-msys2 requires for the augeas Windows build to run inside its MINGW64 env (Git Bash isn't a substitute). Any other shell override must use the workflow default bash.
  • gha-standard-concurrency -- Every GitHub Actions workflow must declare the standard top-level concurrency block exactly: concurrency: group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} This groups runs per workflow per ref, and cancels older in-progress runs on a PR ref only -- so a push to main never cancels its predecessor (lest the merge-queue lose a result), but PR pushes don't burn runner minutes on superseded commits. Deviating from this group/key shape leads to surprising cancellation behaviour.
  • gha-strategy-fail-fast-false -- Any job that declares strategy: (matrix or otherwise) must also set fail-fast: false inside it. GHA's default of fail-fast: true cancels every other matrix entry the moment one fails, which hides the failure modes specific to other OS / version / runner cells -- exactly the matrix cells we added the matrix to exercise. Opt out explicitly: strategy: fail-fast: false matrix: ...
  • inherent-impl-first -- Put a type's inherent impl block before its trait impls in the same module. A reader then meets the type's own constructors and methods first, and the trait impls that follow read as what they are: the type's conformance to interfaces defined elsewhere.
  • new-fn-returns-option -- This function returns Option, and a new one may not. The return type is where the reason a call failed would have gone: Result carries it, Option discards it at the moment it was known. None says "there isn't one" and nothing else -- not which input was wrong, not which file was missing, not which subprocess failed -- so the caller invents an explanation, and a missing tool surfaces three layers up as an empty list rather than as the sentence naming the tool. Return Result with an error that says what happened. Option is for a value that is legitimately absent without anything having gone wrong, which is rarer than it looks. The list below is every function that returned Option when this rule was written. It is a record of what is already here, not a pattern to copy: a name joins it only with the operator's sign-off, and the point of naming them one by one is that adding to it is a decision somebody makes rather than a default anything falls into. Prefer emptying it -- a function that leaves the list has had its failures given names.
  • no-allow-attributes -- #[allow(<lint>)] and #![allow(<lint>)] are forbidden. Use #[expect(<lint>, reason = "...")] (or #![expect(...)] at module / crate scope). #[expect] requires the lint to actually fire, so the suppression is removed automatically when the underlying issue is fixed upstream -- #[allow] can sit silently forever. This rule complements the workspace's clippy::allow_attributes lint, which doesn't fire on inner attributes (#![allow(...)]) -- see rust-lang/rust-clippy#13900.
  • no-assert-eq-bool-literal -- assert_eq!/assert_ne! with a true or false literal as either operand is an assert! written the long way. Assert on the boolean itself -- assert!(x), assert!(!x) -- and when the value is not a bool (a parsed serde_yaml::Value field, say), convert it first: assert_eq!(value.as_bool(), Some(true)) keeps a failure message that shows what the value actually was. This mirrors DeepSource RS-W1024. clippy's bool_assert_comparison covers only operands whose type is bool, so a literal compared against any other type gets past it.
  • no-cargo-crates -- The cargo_* crates (cargo_metadata, cargo_toml, ...) are forbidden. They drive or emulate cargo itself -- most run cargo metadata, which takes the workspace lock -- when what Rust code here needs is a manifest, which the toml crate reads directly.
  • no-cargo-manifest-dir -- CARGO_MANIFEST_DIR is forbidden outside the project-root helper. It points at the crate directory, not the repository root, so reaching repo files through it needs brittle .. hops. Locate the repository root with et_path::find_project_root_from_manifest() in a build.rs (compile time) or edge_toolkit::config::get_project_root() (runtime) instead.
  • no-concat-raw-strings -- concat!(...) of raw-string (r#"..."#) fragments is forbidden -- it is an unreadable way to build one string (a regex, an embedded template) out of a stack of r#"..."#, lines. Use a single multi-line raw string literal instead, or a named const, or -- for a regex -- (?x) verbose mode so the pattern can span lines without concatenation. Plain-string concat! (wrapping a long message, concat!(env!(...), "...")) is fine; only raw-string fragments are banned.
  • no-consecutive-expect -- Consecutive #[expect(...)] attributes on the same item. Merge them into a single #[expect(lint_a, lint_b, reason = "...")] (one attribute can list several lints). Stacking separate #[expect]s -- even with a comment between them -- fragments the suppression and makes it easy to leave one behind when the underlying issue is fixed. Applies to inner #![expect(...)] too.
  • no-current-dir -- std::env::current_dir() is forbidden outside the project-root helper. The current directory is not a reliable anchor for repository files -- it depends on where the binary happens to be invoked. Use edge_toolkit::config::get_project_root() at runtime, or et_path::find_project_root() from a build script at compile time; both locate the repository root via the .dprint.jsonc marker.
  • no-doctest -- Doctests are forbidden. Move runnable examples into a tests/ file (every test file must start with #![cfg(test)]).
  • no-env-macro -- env! / option_env! read a compile-time environment variable and are forbidden for app config -- read configuration through a serde-derived struct deserialised with serde-env (see the no-std-env-var rule and et-ws-runner-common::config) so every variable is declared in one typed place. Only these cargo-provided constants, which have no serde-env equivalent, are exempt: - CARGO_BIN_EXE_<name> -- the built binary's path, injected by cargo for an integration test. - CARGO_PKG_VERSION / CARGO_PKG_NAME -- crate identity baked into the binary. - ET_WIT_DIR -- the WIT directory injected at build time for wit-bindgen. - OUT_DIR -- the build script's output directory, for include!-ing what it generated. Configuration a bare-metal binary bakes in is read there, by its build.rs, and generated as typed constants. CARGO_MANIFEST_DIR is deliberately NOT exempt -- use edge_toolkit::config::get_project_root() at runtime (see the no-cargo-manifest-dir rule).
  • no-env-macro-in-attribute -- env! / option_env! inside an attribute are banned exactly as they are anywhere else, with the same allowlist of cargo-provided constants (CARGO_BIN_EXE_<name>, CARGO_PKG_VERSION, CARGO_PKG_NAME, ET_WIT_DIR, OUT_DIR). Read configuration through a serde-env struct instead, and locate repo files from the project root rather than from CARGO_MANIFEST_DIR.
  • no-env-var-literal -- env::var takes a named constant, never a string literal: a mistyped literal is a lookup that silently fails, while a mistyped constant does not compile. Declare const NAME: &str = "NAME"; beside the read. It is the local twin of DeepSource RS-W1015.
  • no-eq-bool-literal -- ==/!= with a true or false literal as either operand is a boolean written the long way. Use the boolean itself -- x, !x -- and when the value is not a bool (a parsed serde_yaml::Value field, say), convert it and keep the operator: value == true becomes value.as_bool() == Some(true), and value != false becomes value.as_bool() != Some(false). Reach for value.as_bool().unwrap_or_default() only where a value that is not a boolean is meant to read as false. This mirrors DeepSource RS-W1001. clippy's bool_comparison covers only operands whose type is bool, so a literal compared against any other type gets past it.
  • no-expect-used-suppression -- Suppressing clippy::expect_used is forbidden -- .expect("...") messages are not wanted in this codebase, so the workspace-denied lint must stay in force everywhere. Use .unwrap() instead: tests get it for free (clippy.toml's allow-unwrap-in-tests), and non-test code that genuinely must panic suppresses clippy::unwrap_used narrowly with a reason, or better, handles the error. No autofix: these attributes usually list other lints that must be kept, so removing just the clippy::expect_used entry (and rewriting the .expect(...) calls it permitted) is context-dependent.
  • no-extern-crate-self -- extern crate self as <name>; re-introduces the crate's own name as a path root inside its library, letting code write <name>::foo instead of crate::foo / self::foo. Without it, rustc already rejects own-name paths in lib code (E0433); the alias is only needed by proc-macro crates whose generated code emits ::<name>::.... This workspace has none, so use crate:: / self:: for local paths instead.
  • no-inline-mod -- Inline mod X { ... } blocks are forbidden. Move the module body into a separate file (X.rs or X/mod.rs) and declare it with mod X; instead.
  • no-log-crate -- Log through tracing, not the log facade: use tracing::{info, warn}; then info!(...). A log record reaches the OTLP pipeline only through the binaries' bridge, flattened to a string with no structured fields and no span, and mixing the two leaves a crate's logging split between two APIs.
  • no-map-err -- .map_err(...) is forbidden. Convert through From (thiserror's #[from] / #[error(transparent)]) and rely on ?, or define an extension trait whose impl carries the only map_err for the pattern. At actix-web boundaries, derive ResponseError via actix-web-thiserror instead of building actix_web::error::Error* values inside map_err closures.
  • no-metadata-literal -- The string literal "metadata" is forbidden: it is the argument that runs cargo metadata, which takes the workspace lock and so stalls, or is stalled by, every other cargo command running at the same time. Read the workspace from its manifests instead -- the root Cargo.toml's [workspace].members, then each member's own Cargo.toml -- as et-int-gen's HELP.md discovery does. A "metadata" meaning anything else (a Kubernetes object's metadata key, say) takes a one-line ast-grep-ignore naming why.
  • no-mixed-doc-line-comments -- Doc comment (///) is adjacent to a regular // comment. Either turn the regular comment into a doc comment (///) or move it away from the doc block -- readers expect a contiguous /// run to be the item's rustdoc.
  • no-mod-in-tests -- mod is forbidden inside test files (tests/**/*.rs and src/test_*.rs). Keep shared test helpers in a test-support library crate -- et-test-helpers for low-dependency helpers, or a dedicated crate such as et-ws-test-server / et-test-otlp -- and use them, rather than a tests/common/mod.rs include or an inline mod { ... }. A library gives each helper one compiled home (so coverage and lints see it once) instead of recompiling it per test binary, and it keeps integration-test files flat: one file per test binary.
  • no-non-ascii -- Non-ASCII character in a string literal or doc comment. Stick to ASCII in source: use -- for an em-dash, -> for an arrow, and straight quotes for curly quotes, etc. ASCII-only source keeps terminals, log scrapers, and grep reading the same bytes the editor shows.
  • no-relative-path-literal -- Relative path literals ("." / ".." / "./..." / "../...") are forbidden: they hardcode a location relative to the process's current directory or this crate's directory, which is brittle. Anchor paths to the repository root instead -- edge_toolkit::config::get_project_root() (runtime) or et_path::find_project_root_from_manifest() (build scripts) -- and join the remainder onto it. (Char literals like '.' for splitting are unaffected.)
  • no-repeated-wildcard-pattern -- Two or more _ side by side in a tuple or tuple-struct pattern each ignore one field where .. ignores them all. Write the run as ..: (outcome, _, _, _) becomes (outcome, ..), (_, _, path) becomes (.., path), and (outcome, _, _, closed) becomes (outcome, .., closed). The pattern then says which fields matter rather than counting the ones that do not, and it keeps compiling when one is added. This mirrors DeepSource RS-W1213; clippy has no equivalent.
  • no-result-alias -- Defining a type Result<...> alias shadows std::result::Result at file scope and makes every Result<T> ambiguous to readers. Spell the error type at each call site instead (Result<T, my::Error>).
  • no-result-string-err -- Result<_, String> flattens any typed error to a free-form string at the point of failure -- losing the source variant, the ability to branch on it, and the chain of causes. Define a thiserror enum (or reuse one of the workspace's *Error types) and return that instead.
  • no-self-call-in-default -- A Default::default body may not delegate to another constructor of Self (Self::new(), Self::from_parts(..)) (DeepSource RS-A1008). Whenever that constructor is, or later becomes, written in terms of Self::default() -- the usual shape for a builder or a new that fills in the rest -- the two call each other until the stack overflows, and nothing at compile time says so. Build the value directly in default(), and let the other constructors call it rather than the reverse.
  • no-shadow-result -- Don't shadow std::result::Result at file scope -- every bare Result<T> then becomes ambiguous to readers. Either spell the error type at each call site (Result<T, my::Error>), reference the local alias by its qualified path (crate::Result<T>, et_int_gen::Result<T>), or alias the import (use foo::Result as FooResult;).
  • no-spawn-cargo -- Rust code must not run cargo or any cargo subcommand binary (cargo-<subcommand>) as a subprocess. A nested cargo competes for the same build lock and target directory as whatever invoked it, so it stalls behind or rebuilds over the outer build, and it ties the code to a toolchain being on PATH. Read what you need from the manifests, link the crate whose data you want, or run cargo from a mise task instead. A test that genuinely has to build another workspace binary takes a one-line ast-grep-ignore naming why.
  • no-std-env-var -- std::env::var / std::env::var_os is forbidden for app config. Read configuration through a serde-derived struct deserialised with serde-env (see the runner config modules and et-ws-runner-common::config), so every variable is declared in one typed place with defaults and validation. Exemptions, intentionally narrow: - RUST_* (e.g. RUST_LOG, consumed by tracing) -- diagnostics. - ET_TEST_* -- test-only switches; production binaries must NOT branch on these. The integration test in services/ws-wasi-runner/tests/ sets one to opt the spawned runner into a macOS-only fast-exit path. - MISE_* (e.g. MISE_ENV) -- the mise tool's own state, not app config; reading these lets helpers like mise_env_includes reflect tooling behaviour without round-tripping through a struct. Every exemption above is a name prefix, which keeps it self-limiting: a variable has to be named into the exemption to use it. A bare variable name would not be -- it would open that read to every file in the repo, including production code that has a config struct to put it in. PATH was such an entry, and is now a one-line ast-grep-ignore on the single test helper that shapes PATH for a stand-in.
  • no-std-fs -- std::fs is forbidden -- its io::Errors carry no path, so a failure reads "No such file or directory" with no clue which file. Use fs_err (fs-err workspace dep), whose drop-in wrappers attach the offending path to every error. Import it as use fs_err as fs; and call fs::read, fs::write, fs::File::open, etc., exactly as you would std::fs.
  • no-string-literal-line-continuation -- Backslash-newline inside a Rust string literal is a line continuation (the \ + newline + leading whitespace all get elided at parse time). Banned per the repo-wide no-trailing-backslash rule. Use concat!("a", "b") to assemble a multi-line string literal, keep it on one line, or drop to a raw string r"..." if the content really needs literal backslashes at line ends. A generic-regex rule catches the same pattern repo-wide; this variant is the Rust-AST-aware companion, and runs faster.
  • no-string-new -- String::new() is banned (DeepSource RS-W1079): construct empty strings with String::default() instead, so the zero value routes through the Default impl like every other default-constructed value in the codebase.
  • no-test-in-python-docstrings -- Docstring in non-test code mentions tests. Document what the code does for every caller; why a test needs it belongs in the test that relies on it. A name containing it (test, ws-test-server, ET_TEST_X) is allowed.
  • no-test-in-rust-docs -- Doc comment in non-test code mentions tests. Document what the item does for every caller; why a test needs it belongs in the test that relies on it. A name containing it (test, ws-test-server, ET_TEST_X) is allowed.
  • no-uds-listener -- HTTP servers here listen on TCP only. A Unix-domain-socket listener gives requests no peer address, and the handlers take the client address from the socket peer rather than from client-supplied Forwarded / X-Forwarded-For headers -- so ws_handler refuses every request arriving on one. Bind a TCP address instead.
  • no-unwrap-or-call -- .unwrap_or(...) evaluates its argument even when the value it stands in for is there, so a call written there runs every time. Reach the fallback another way: let Some(value) = option else { ... } or a match, or .unwrap_or_else(...) when the fallback is real work -- clippy's unnecessary_lazy_evaluations objects to a closure around a plain constructor. This mirrors DeepSource RS-W1031. clippy's or_fun_call lets an enum or tuple-struct constructor such as Ok(None) through as cheap, so a constructor here gets past clippy alone.
  • org-identity-lives-in-et-org -- This constant spells out the organisation name or the crate prefix. Both belong to et-org, which is the one place either literal is written: take the value from et_org::ORG, et_org::CRATE_PREFIX, et_org::NPM_SCOPE, et_org::REPOSITORY_URL or et_org::IMAGE_REGISTRY, or build a longer string from them, rather than writing the name out again here. These strings are the same fact wearing different clothes -- the npm scope, the container registry, the repository URL and the crate prefix all move together if the project is renamed -- so a copy living outside et-org is a copy that gets missed. et-org writes the organisation exactly once and assembles the rest from it, which is what makes that rename a one-line change. A concat! cannot take a constant, so a call site that has to embed the URL at compile time uses et_org::repository_url!(); et_org::org!() is there for the same reason. This does not apply to a specific name that merely starts with the prefix. et-ws-server names one crate and et-path-prefix-probe names one test fixture; neither is the project's identity, and neither changes because the organisation did. Only the bare prefix "et-" and any string carrying the organisation name are covered. Tests are deliberately not exempt from the definition rule, but asserting a literal is not defining one: a test that pinned expected output against et_org::NPM_SCOPE would keep passing if that constant were wrong, so those assertions stay spelled out.
  • prefer-self-mod-use-bare-chain -- This file declares mod $X; and references it via use $X::.... Write use self::$X::... to make the local reference explicit.
  • prefer-self-mod-use-bare-group -- This file declares mod $X; and references it via use $X::{...}. Write use self::$X::{...} to make the local reference explicit.
  • prefer-self-mod-use-pub-chain -- This file declares mod $X; and re-exports it via pub use $X::.... Write pub use self::$X::... to make the local reference explicit.
  • prefer-self-mod-use-pub-group -- This file declares mod $X; and re-exports it via pub use $X::{...}. Write pub use self::$X::{...} to make the local reference explicit.
  • prefer-self-mod-use-pubcrate-chain -- This file declares mod $X; and re-exports it via pub(crate) use $X::.... Write pub(crate) use self::$X::... to make the local reference explicit.
  • prefer-self-mod-use-pubcrate-group -- This file declares mod $X; and re-exports it via pub(crate) use $X::{...}. Write pub(crate) use self::$X::{...} to make the local reference explicit.
  • prefer-self-mod-use-pubsuper-chain -- This file declares mod $X; and re-exports it via pub(super) use $X::.... Write pub(super) use self::$X::... to make the local reference explicit.
  • prefer-self-mod-use-pubsuper-group -- This file declares mod $X; and re-exports it via pub(super) use $X::{...}. Write pub(super) use self::$X::{...} to make the local reference explicit.
  • python-process-control-except -- A bare handler or BaseException catch can intercept shutdown and cancellation signals, including KeyboardInterrupt and SystemExit. Catch specific operational exceptions instead, or use straight-line cleanup ending in a bare raise. Check-only: choosing the correct exception types requires knowledge of the operation being protected.
  • semgrep-path-anchored (meta) -- Every semgrep paths.include / paths.exclude entry starts with /, anchoring it at the repository root. A pattern without one is easy to get wrong in either direction -- an exclusion that lost its slash silently matches nothing, while a bare *.rs matches at any depth only because of a rule nobody reading the config has to remember -- so the anchor is written out every time: /**/*.rs for any depth, /verification/** for one tree. This rule takes the patterns that match at any depth -- a bare name, or one opening with **/ -- and its autofix rewrites each to the same match written from the root, /**/<name>. A pattern with a / further in is already anchored at the root, and semgrep-path-rooted takes those.
  • semgrep-path-rooted (meta) -- A semgrep paths.include / paths.exclude entry with a / partway through matches from the repository root already, so it is written with the leading / that says so: /verification/published/**, not verification/published/**. The rule's autofix adds it. The patterns that match at any depth are semgrep-path-anchored's, which writes them as /**/<name>.
  • use-mod-order -- Order top-level items: external use (std / third-party) first, then mod / pub mod declarations, then local use self:: / crate:: / super::. So an external use must not appear after a mod, and a self/crate/super use must not appear before a mod.
  • use-tracing-imports -- Import what you use from tracing and call it by its bare name: use tracing::{info, warn}; then info!(...), use tracing::instrument; then #[instrument(...)]. A tracing:: path at the use site hides the dependency from the file's imports, and mixing the two spellings in one crate makes the logging calls harder to find and change.
  • verification-no-args -- A generated deployment must not set a container's args. Configure the program through its environment instead -- every service here reads its settings through serde-env -- or, for an upstream image, run its own entrypoint and set whatever it reads from the environment. Kubernetes args replaces the image's CMD outright, so an args entry also silently discards the arguments the image was built to start with.
  • write-ending-newline -- A write! whose format string ends in \n is a writeln! written the long way: use writeln! and drop the final \n. This mirrors DeepSource RS-C1005. clippy's write_with_newline covers only a single trailing newline in a plain literal: it skips a string ending in \n\n, and one assembled with concat!, which are the cases this rule exists to catch.

cargo check

  • cargo-check (default env; run by check:rust).

cargo clippy

  • cargo-clippy-check (default env; run by check:rust). Repaired by cargo-clippy-fix. Config: Cargo.toml, config/clippy.toml.
  • cargo-clippy-check-esp32c6 (rust env; not run by mise run check) -- Lint the ESP32-C6 firmware for its own target. Config: Cargo.toml, config/clippy.toml.
  • cargo-clippy-check-pico-plus2w (rust env; not run by mise run check) -- Lint the Pimoroni Pico Plus 2 W firmware for its own target. Config: Cargo.toml, config/clippy.toml.
  • cargo-clippy-check-pico2w (rust env; not run by mise run check) -- Lint the Raspberry Pi Pico 2 W firmware for its own target. Config: Cargo.toml, config/clippy.toml.
  • cargo-clippy-check-pkg (default env; not run by mise run check) -- cargo clippy on one or more workspace packages (narrow inner-loop check). Config: Cargo.toml, config/clippy.toml.

Rules, from Cargo.toml:

cargo deny

  • cargo-deny-check (default env; not run by mise run check) -- Audit dependencies with cargo-deny (advisories, bans, licenses, sources). Config: config/deny.toml.

cargo doc

  • cargo-doc-check (default env; run by check:rust) -- Type-check rustdoc with -D warnings to fail on any doc issues.

cargo fmt

  • cargo-fmt-check (default env; run by check:rust). Repaired by cargo-fmt. Config: .rustfmt.toml.

cargo hack

  • cargo-hack-check (default env; not run by mise run check) -- cargo clippy every crate's feature powerset.

cargo metadata

  • cargo-clippy-wasm-check (default env; run by check:rust) -- cargo clippy the browser and WASI crates on their wasm targets.

cargo shear

  • cargo-shear-check (default env; run by check:rust) -- Find unused and misplaced Cargo dependencies with cargo-shear. Repaired by cargo-shear-fix.

cargo unmaintained

  • cargo-unmaintained-check (default env; not run by mise run check) -- Flag dependencies whose upstream repo is archived or unmaintained.
  • cargo-unmaintained-check-pkg (default env; not run by mise run check) -- Check one dependency with cargo-unmaintained (cheap re-check of a single finding).

clang-format

  • clang-format-check (zig env; run by check:zig) -- Check C/C++ source formatting (clang-format). Repaired by clang-format. Config: config/clang-format.yaml.

clang-tidy

  • clang-tidy-check (zig env; run by check:zig) -- Lint C/C++ sources (clang-tidy). Repaired by clang-tidy-fix. Config: config/clang-tidy.yaml.

conftest

  • checks-md-check (default env; run by check:rust) -- Fail if CHECKS.md was last generated more than 7 days ago (run mise run gen:checks) (conftest). Config: config/conftest/policy.
  • conftest-check-ast-grep-rules (default env; run by conftest-check) -- Check every ast-grep rule declares severity error (conftest). Config: config/ast-grep/rules, config/conftest/policy.
  • conftest-check-branch-size (default env; run by conftest-check) -- Fail when the branch modifies more files than CodeRabbit reviews in one pull request (conftest). Config: config/conftest/policy.
  • conftest-check-checks-badge (default env; run by conftest-check) -- Check the README checks badge keeps step with CHECKS.md's rules count (conftest). Config: config/conftest/policy.
  • conftest-check-dockerfile (default env; run by conftest-check) -- Cross-check Dockerfiles against mise [tools] pins + heredoc body-first-line invariants. Config: config/conftest/policy.
  • conftest-check-dockerignore (default env; run by conftest-check) -- Check .dockerignore keeps every top-level Markdown file out of the Docker build context (conftest). Config: config/conftest/policy.
  • conftest-check-generated-trees (default env; run by conftest-check) -- Cross-check config/generated-trees.toml against the linter configs it governs (conftest). Config: config/ast-grep/rules, config/conftest/policy, config/generated-trees.toml, config/jscpd.json, config/pyrefly.toml, config/semgrep.
  • conftest-check-jscpd (default env; run by conftest-check) -- Check the jscpd baseline has not grown (conftest). Config: config/conftest/policy, config/jscpd-baseline.json.
  • conftest-check-mise-templates (default env; run by conftest-check) -- Check no mise config value contains Tera's comment opener (conftest). Config: config/conftest/policy.
  • conftest-check-no-exemptions (default env; run by conftest-check) -- Check the rules held to every file carry no path exemption, and their inline guard (conftest). Config: config/ast-grep/rules, config/conftest/policy, config/semgrep.
  • conftest-check-policy-pairs (default env; run by conftest-check) -- Check every conftest policy has a test file beside it (conftest). Config: config/conftest/policy.
  • conftest-check-policy-tests (default env; run by conftest-check) -- Run the Rego unit tests beside the conftest policies (conftest verify). Config: config/conftest/policy.
  • conftest-check-toml (default env; run by conftest-check) -- Run conftest OPA/Rego policies over the TOML config + lock files. Config: config/cargo-features.toml, config/conftest/policy, config/deny.toml, config/pyrefly.toml, config/upstream-cache/data.toml.
  • conftest-check-yaml (default env; run by conftest-check) -- Run conftest OPA/Rego policies over the GitHub Actions YAML, and the backslash ban over all YAML. Config: config/conftest/policy.

Rules, from config/conftest/policy:

  • advisories (config/conftest/policy/advisories/advisories.rego):
    • Every advisory ignore must carry a reason = "expires YYYY-MM-DD" review date.
    • Nanoseconds in a day, added to the parsed midnight so an entry survives the whole of its review date.
    • Every advisory ignore expires on one date, the next milestone review, which the operator sets.
  • ast_grep_rules (meta) (config/conftest/policy/ast_grep_rules/ast_grep_rules.rego):
    • A rule whose severity is anything but error, or is missing, is not a gate.
  • branch_size (config/conftest/policy/branch_size/branch_size.rego):
    • A branch may modify at most max_files files, or CodeRabbit skips its pull request's review.
  • cargo (config/conftest/policy/cargo/cargo.rego):
    • The root Cargo.toml must not depend on a crate this repo has banned.
    • Member crates: no path deps, no wildcard versions, no inline git deps.
    • A member crate must not take a dependency at a wildcard version.
    • A member crate must not take an inline git dependency.
    • Member [dependencies]/[dev-dependencies] must inherit via workspace = true so pins stay in [workspace.dependencies].
    • A crate with a [lib] must disable the doctest harness and must not rename the lib.
    • A member crate's [lib] must not rename the library away from the package name.
    • Every member must inherit the workspace lint tables.
    • Every member crate must be registered in the root [workspace].members.
    • Shared [package] metadata must be inherited from [workspace.package] via <field>.workspace = true.
    • Crate names are namespaced: "edge-toolkit" or "et-" for normal crates, "int-" marking an internal one.
    • publish is stated in every manifest rather than left to cargo's implicit default.
    • The "int-" marker and publishability are the same fact, so the name and the flag must agree both ways.
    • A crate without the int- marker must set publish = true.
    • A path dependency's version must match the version its crate actually declares.
    • A root workspace dependency must be pinned to major.minor, not a patch version, unless it is excepted.
    • The exception map is a two-way contract.
    • crates.io rejects an upload whose manifest carries no description, so every publishable crate has one.
    • An empty features = [] on a dependency is pointless noise -- drop it.
    • A member crate's feature must not share its name with one of its dependencies.
    • A member crate's feature must be one config/cargo-features.toml lists.
    • config/cargo-features.toml lists no feature that no crate declares any longer.
    • No manifest may depend on a cargo_* / cargo-* crate (cargo_metadata, cargo_toml, ...).
    • Crates log through tracing, so none may depend on the log facade directly.
    • tokio's full feature is banned on every dependency spec, the workspace table included.
    • The root tracing dependency must enable log.
    • A crate that declares a feature must publish the feature list on docs.rs through document-features.
    • Dependency overrides ([patch]/[replace]) belong in the root manifest, not a member crate.
  • dependencies (config/conftest/policy/cargo/dependencies.rego):
    • No crate may depend on an internal one.
    • A standalone crate must not depend on any of this repo's et- crates.
    • No crate under services/ may depend on clap.
  • checks_badge (meta) (config/conftest/policy/checks_badge/checks_badge.rego):
    • CHECKS.md must record its rules count, which the badge is held to.
    • README.md must define the checks badge.
    • The checks badge records the rules count rounded down to a multiple of 20, so it moves with every 20 rules added.
  • checks_md (config/conftest/policy/checks_md/checks_md.rego):
    • CHECKS.md must say when it was generated, which only the generator writes.
    • A document stamped more than once was not written by the generator, which stamps it exactly once.
    • A stamp the shape allows but the calendar does not, such as 2026-02-30, was written by hand and has no age.
    • The document is regenerated at least once every max_age_days days.
    • The generator stamps today's UTC date, so a later one was written by hand.
  • checksums (config/conftest/policy/checksums/checksums.rego):
    • Every _asset var in the mise configs must have an [asset] table in config/upstream-cache/data.toml.
    • Every [asset] table in config/upstream-cache/data.toml must be referenced by a mise _asset var.
    • Per-entry shape check on each asset table.
    • Every recorded asset must name its upstream project URL.
    • Every recorded asset must carry its license as an SPDX expression.
    • Every recorded asset must carry a sha256, left empty only while bootstrapping.
  • pyrefly_python_sync (config/conftest/policy/cross/pyrefly_python_sync.rego):
    • pyrefly must pin the Python version it type-checks against.
    • pyrefly's pinned Python version must equal the mise python pin, the version every lane installs.
  • wasm-bindgen-sync (config/conftest/policy/cross/wasm-bindgen-sync.rego):
    • The mise wasm-bindgen pin must match the wasm-bindgen version Cargo.lock resolves.
  • dockerfile (config/conftest/policy/dockerfile/dockerfile.rego):
    • A version a Dockerfile hard-codes must match the [tools] pin for the same tool.
    • Every pipx:* tool in an in-scope config must be in Dockerfile.nanoserver's MISE_DISABLE_TOOLS.
    • RUN heredocs must invoke bash and use a QUOTED delimiter (RUN bash <<'EOF'). Two-part rule.
    • A RUN heredoc must quote its delimiter so bash, not the outer shell, does the expansion.
    • A Dockerfile apt-get install must pass --no-install-recommends on the same line.
    • A Dockerfile must use apt-get, never apt.
    • A renamed or removed ARG would leave the comparison undefined, which reads as a pass; fail instead.
    • [bootstrap.packages] must list every package the Dockerfile ARGs install.
    • [bootstrap.packages] must not list a package the Dockerfile ARGs do not install.
    • Dockerfile.nanoserver is exempt: it deliberately loads a subset, because pipx tools cannot run on Nano Server.
    • Every other Dockerfile's COMMON_PACKAGES must be drawn from the root Dockerfile's list.
    • A Dockerfile's MISE_VERSION must match .mise/config.toml's min_version.
  • dockerfile_heredoc (config/conftest/policy/dockerfile_heredoc/dockerfile_heredoc.rego):
    • A RUN heredoc's first body line must be set -euo pipefail.
  • dockerfile_stages (config/conftest/policy/dockerfile_stages/dockerfile_stages.rego):
    • A --from= naming no stage declared before it would pull an image of that name.
  • dockerignore (config/conftest/policy/dockerignore/dockerignore.rego):
    • .dockerignore must exclude every top-level Markdown file.
    • Nothing after the last *.md may bring a top-level Markdown file back into the context.
  • generated_trees (meta) (config/conftest/policy/generated_trees/generated_trees.rego):
    • Every config a generated tree requires must carry that tree's exclusion pattern.
    • Reverse direction, matched on the tree prefix rather than on the exact pattern.
    • A required_in entry that no [anchor] key covers would make the forward rule above vacuous.
  • gha (config/conftest/policy/gha/gha.rego):
    • Every workflow must set the default run shell so steps run in bash on every runner without a per-step shell:.
    • Steps must not override the shell -- rely on the workflow default.
    • Every workflow must declare MISE_ENV at the workflow level so the set of loaded language envs is visible at a glance.
    • The MISE_ENV VALUE must be the full guest-language set.
    • DOCKER_BUILDKIT=1 must not be set in the docker-windows workflow.
    • The docker-windows workflow must not set DOCKER_BUILDKIT at job scope either.
    • Nor at step scope.
    • A workflow's trigger paths must cover every local action it uses, so an edit to one re-runs it.
    • A workflow step's apt-get install must pass --no-install-recommends on the same line.
    • A workflow step must use apt-get, never apt.
    • Every actions/upload-artifact step must set with.if-no-files-found: error.
    • Scalar lists: matrix.<key>: [a, b, c].
    • include: entries, which is where the per-combination keys live.
    • Every job must say how long it gets.
    • No job may be given more than 150 minutes.
  • gha_action (config/conftest/policy/gha_action/gha_action.rego):
    • Composite-action run: steps require an explicit shell:.
    • Every actions/upload-artifact step must set with.if-no-files-found: error.
  • gha_combined (config/conftest/policy/gha_combined/gha_combined.rego):
    • Every workflow must trigger on pull_request so PR CI runs it when relevant code changes.
    • Every workflow must support manual workflow_dispatch: re-running a failed/flaky run shouldn't need a no-op commit.
    • When a workflow scopes its pull_request trigger by paths, the workflow's own YAML must be in that list.
  • gha_mise (config/conftest/policy/gha_mise/gha_mise.rego):
    • Every setup-dotnet dotnet-version must equal the mise dotnet pin.
    • Any workflow string embedding a mise install path must track the matching [tools] pin.
    • A workflow's env.MISE_ENV must match .mise/config.toml's ALL_LANGS.
  • gha_uses (config/conftest/policy/gha_uses/gha_uses.rego):
    • A step must not pass an input that the local action it calls does not declare.
    • Matched with contains rather than startswith, since the root need not begin the string.
  • jscpd (meta) (config/conftest/policy/jscpd/jscpd.rego):
    • Module-scope sprintf templates, each on its own line so the source stays inside the line limit.
    • declared_clones may sit at most max_slack above the baseline, so progress gets banked.
  • mise (config/conftest/policy/mise/mise.rego):
    • A task run must be a string, not an array.
    • A multiline run must use shell = "{{ vars.task_shell }}".
    • Task descriptions must be single-line.
    • compgen is a bash builtin that busybox-w32 ash (Nano Server's shell) does not provide.
    • A task run must not embed version-like text (an X.Y.Z literal), comments included.
    • A cargo: tool must not build from source unless allowlisted or os-scoped to a second-tier platform.
    • ubi: is deprecated; the http: backend replaces it.
    • An http: tool must not fetch from a forge URL; use the github: backend instead.
    • No per-platform URL of an http: tool may point at a forge either.
    • A tool must not be os-scoped unless allowlisted, since every tool must work on every OS.
    • A mise install path that a config string embeds must name the version [tools] pins.
    • python must be pinned to a full version triple (X.Y.Z), not a minor alias.
    • Each OS's preinstall task must read its package lists from the Dockerfile ARGs.
    • Every usage arg a task declares must be read by its run body.
    • The uppercase spelling is a no-op everywhere except Windows, so it is rejected on sight.
  • mise_lock (config/conftest/policy/mise_lock/mise_lock.rego):
    • A tool locks a glibc Linux build without an allowlist entry.
    • An allowlist entry names a tool that no longer locks a glibc build anywhere.
  • no_exemptions (meta) (config/conftest/policy/no_exemptions/no_exemptions.rego):
    • An unexemptable semgrep rule may not set paths, which is how semgrep narrows the files a rule scans.
    • An unexemptable ast-grep rule may not set files or ignores, the two ways ast-grep narrows a rule.
    • The inline guard must name every unexemptable rule, or suppressing one added later would go unflagged.
    • The inline guard must exist, since without it a suppression comment is an exemption nothing reports.
  • no_trailing_backslash (config/conftest/policy/no_trailing_backslash/no_trailing_backslash.rego):
    • Flag any string leaf containing \ followed by \n (LF), reporting its path key in the message.
  • policy_pairing (meta) (config/conftest/policy/policy_pairing/policy_pairing.rego):
    • Every policy must have a test file beside it, or a reasoned untested_policy entry.
    • The map is a two-way contract, the same way a lint suppression is.
    • An entry naming a policy that is no longer there has outlived what it described.
  • pyproject (config/conftest/policy/pyproject/pyproject.rego):
    • A path = ".." uv source points at the parent directory, which isn't a package -- almost always a mistake.
    • A pyproject's uv_build requirement must pin the uv version mise installs.
  • tera_comment (config/conftest/policy/tera_comment/tera_comment.rego):
    • A mise config value must not contain {#, which Tera reads as the start of a comment.

cpplint

  • cpplint-check (zig env; run by check:zig) -- Lint C/C++ sources for Google C++ style (cpplint).

dart

  • dart-check (dart env; run by check:dart). Repaired by dart-fix.
  • dart-fmt-check (dart env; run by check:dart). Repaired by dart-fmt.

dotnet

  • dotnet-check (dotnet env; run by check:dotnet) -- Check dotnet C# formatting. Repaired by dotnet-fmt.

dprint

  • dprint-check (default env; run by check:rust). Repaired by dprint-fmt. Config: .dprint.jsonc, config/dprint.jsonc.

editorconfig-checker

  • editorconfig-check (default env; run by check:rust). Config: .editorconfig.

et-int-gen

  • gen-help-all (default env; run by check:rust) -- Regenerate HELP.md for every binary crate using clap or reading serde-env config.
  • gen-help-check (default env; run by check:rust) -- Fail if any checked-in HELP.md is stale vs its clap tree or env config (run mise run gen-help-all).

et-int-repo-check

  • repo-check (default env; run by check:rust) -- Repository-wide checks that need to read git itself (commit hashes, ...).

flawfinder

  • flawfinder-check (zig env; run by check:zig) -- Scan C/C++ sources for risky C APIs (flawfinder security lint).

gitleaks

  • gitleaks-check (default env; run by check:rust) -- Scan the working tree for committed secrets with gitleaks. Config: config/gitleaks.toml.

gocloc

  • sloc-crosscheck (coverage env; not run by mise run check) -- Re-count sloc's file lists with gocloc, an independent implementation of cloc's rules.

hadolint

  • hadolint-check (default env; run by check:rust) -- Lint the Dockerfiles with hadolint. Config: config/hadolint.yaml.

jscpd

  • jscpd-check (default env; run by check:rust) -- Detect copy/pasted code across every language (jscpd). Config: config/jscpd-baseline.json, config/jscpd.json.

kubeconform

  • kubeconform-check (default env; run by check:rust) -- Validate the generated Kubernetes manifests against the Kubernetes API schemas.

ls-lint

  • ls-lint-check (default env; run by check:rust) -- Lint file and directory naming conventions (ls-lint). Config: config/ls-lint.yaml.

lychee

  • link-check (default env; run by check:rust) -- Check that URLs in prose, source and config files are reachable (network). Config: config/lychee.toml.

osv-scanner

  • osv-scanner-check (default env; not run by mise run check). Config: config/osv-scanner.toml.

oxfmt

  • oxfmt-check (js env; run by check:js) -- Check JS/TS formatting (oxfmt). Repaired by oxfmt-fmt. Config: config/oxfmtrc.jsonc.

oxlint

  • oxlint-check (js env; run by check:js) -- Lint JavaScript/TypeScript sources (oxlint). Repaired by oxlint-fix. Config: config/oxlintrc.jsonc, config/tsconfig.json.

pyrefly

  • pyrefly-check (python env; run by check:python). Config: config/pyrefly.toml.

regal

  • regal-check (default env; run by check:rust) -- Lint Rego policy files with Regal (style, idiomatic, bugs). Repaired by regal-fix. Config: config/conftest/policy, config/regal.yaml.

roslynator

  • roslynator-check (dotnet env; run by check:dotnet) -- Analyze C# sources (Roslynator). Repaired by roslynator-fix.

ruff

  • ruff-check (python env; run by check:python). Repaired by ruff-fix. Config: ruff.toml.
  • ruff-fmt-check (python env; run by check:python). Repaired by ruff-fmt. Config: ruff.toml.

rumdl

  • rumdl-check (default env; run by check:rust) -- Lint every tracked Markdown file (rumdl). Repaired by rumdl-fix. Config: config/rumdl.toml.

ryl

  • ryl-check (default env; run by check:rust) -- Lint YAML with ryl (a yamllint-compatible Rust linter). Config: config/ryl.toml.

semgrep

  • semgrep-check (default env; run by check:rust) -- Run Semgrep code and configuration rules. Repaired by semgrep-fix. Config: .semgrepignore, config/semgrep.
  • semgrep-check-rule-tests (default env; run by check:rust) -- Run the Semgrep rule tests against their annotated cases (semgrep --test). Config: .semgrepignore, config/semgrep, config/semgrep-tests.

Rules, from config/semgrep:

  • anyhow-only-in-error-rs -- anyhow may only be named in an error.rs, in a #[from] variant of the crate's error enum. Domain code returns that enum and relies on ?, so an anyhow::Error never reaches a public signature or a match arm. If a foreign API hands you one here, add the #[from] variant to this crate's error.rs and let ? do the conversion.
  • bare-workspace-inline-dep -- Use the dotted form dep.workspace = true instead of the inline table dep = { workspace = true }. (Inline tables with extra keys, e.g. dep = { workspace = true, features = [...] }, are fine.)
  • cargo-feature-needs-doc-comment -- Document every cargo feature with a ## comment on the lines directly above it. document-features renders those comments as the crate's feature list on docs.rs, which is where users choose features, so an undocumented feature is one they cannot learn about without reading the manifest. A plain # comment is not rendered; turn it into ## user-facing prose, and mark a feature only the repo's own tooling uses with Internal:.
  • cargo-table-order -- Order a crate manifest's tables as every other crate in the workspace does: [package] and its [package.metadata.*] subtables, [lib], [[bin]], [[example]], [[test]], [[bench]], [features], [dependencies], [dev-dependencies], [build-dependencies], then [lints]. A target.<cfg> dependency table follows its plain one. One order means a reader finds a table without scanning for it, and a diff that adds one lands in the same place in every crate. Move the reported table to its slot.
  • comment-summary-line -- A multi-line # comment block must open with a single summary line ending in a full stop (PEP 257 style). The summary describes the whole block in one sentence; continuation lines follow. End the first comment line with a ..
  • comment-summary-line-c -- A multi-line C-style comment (a /* */ block or a // run) must open with a single summary line ending in a full stop (PEP 257 style). The summary describes the whole block in one sentence; continuation lines follow. End the first comment line with a ..
  • deepsource-code-must-name-its-analyzer -- Write a DeepSource finding as DeepSource RS-W1079, naming the analyzer in front of the code. A bare code is unreadable to anyone who does not already know whose it is: RS-, JS- and CXX- look like they could belong to any of the analyzers this repo runs, and a search for one turns up nothing unless the searcher guesses the vendor first. The exception is a skipcq: pragma, whose grammar DeepSource defines -- leave the code bare there, and let the surrounding comment carry the reason.
  • dockerfile-heredoc-set-euo-pipefail -- Dockerfile RUN heredocs must begin with set -euo pipefail as the first body line. BuildKit's default heredoc shell carries neither -e nor -o pipefail (and dash on Debian/Ubuntu doesn't support -o pipefail at all), so the strict-mode invariant has to be re-declared per body. Paired with the policy that requires RUN <<EOF bash for the interpreter.
  • dockerfile-no-banner-comment -- Decorative # --- text --- banner comments are not allowed in Dockerfiles. Use a plain, concise one-line comment (# text) instead: drop the surrounding dash runs, keeping the inner text.
  • ls-lint-no-glob -- ls-lint config entries must be literal paths -- ** (and any other glob syntax) is banned. ls-lint snapshots glob-based ignores once at startup and deletes every key that matches nothing at that moment (internal/glob/glob.go), so a glob covering a directory that a parallel task creates mid-run silently stops applying and ls-lint walks the tree it was supposed to skip. That is how **/.gradle let modules/web/kotlin-data1/.gradle/9.7.0 reach the .dir rules while check:kotlin ran alongside ls-lint-check. A literal path is never expanded, so it holds no matter when the directory appears, and one naming a path that never appears costs nothing. List each directory explicitly, adding a new entry per module rather than reaching for a pattern that covers them all.
  • mise-task-coreutils-prefix -- Call POSIX utilities in mise task bodies through the mise-managed uutils multicall, as coreutils <util>. A bare mkdir/cp/rm/... runs whatever the host ships -- BSD on macOS, GNU on Linux, busybox or Git Bash on Windows -- so flags and behaviour differ per machine, and the task depends on a tool mise does not pin. A command that is run by another shell (inside a container or pod) is a genuine exception: suppress it on that line with a nosemgrep comment saying where it runs.
  • module-description-length -- A ws-module description is the module-dropdown label on the ws-server index page; keep it at most 40 characters so the dropdown stays readable.
  • multiline-literal-string-needs-backslash -- Write a multi-line string without a backslash as """...""", not '''...'''. A TOML literal string exists to keep backslashes verbatim (Windows paths, regex escapes); without one it only makes this value differ from the """ bodies around it, and the next reader looks for an escape that is not there.
  • multiline-string-closes-on-own-line -- Close a multi-line """ (or ''') string with the delimiter alone on its own line, not on the end of its last line. Every multi-line value then ends the same way and appending a line never means editing the one before it. In a Tera value, end the last tag with -%} (e.g. {%- endif -%}) so the newline before the delimiter is trimmed and the rendered value is unchanged.
  • multiline-task-run-needs-bash-shell -- A mise task with a multiline run must be immediately followed by shell = "{{ vars.task_shell }}". Put shell on the line right after the closing """/''', so a failing command fails the task instead of being masked. task_shell_trace is also accepted (xtrace, used on the Windows OS-specific preinstall for failure-diagnosis transcripts); task_shell_plain is not, since it carries no -e. bash is installed before any task on Windows (see preinstall), so even the setup tasks can use it.
  • no-action-validator-mentions -- Do not name the workflow schema validator outside the files that install and run it. Whether a workflow key is spelled a certain way to keep a validator happy is not what a reader of that workflow needs; say what the key does.
  • no-actionlint-mentions -- Do not name the GitHub Actions linter outside the files that install and run it. A workflow comment crediting it for a shell quoting or expression change says nothing a reader of the workflow can use.
  • no-ast-grep-mentions -- Do not name the structural search tool outside its own ruleset and the configs that scope it. Pointing at one of its rules from the code that rule governs duplicates the rule's own message, which is where the explanation belongs and where it stays correct.
  • no-build-task-name-mentions -- Do not name a module build task outside the images, jobs and READMEs that run it. A skip message telling a developer which build to run is the exception, and the reason it works is that it is addressed to someone at a terminal; a comment in the module's own source is not.
  • no-cargo-deny-mentions -- Do not name the dependency auditor outside its config and the job that runs it. A dependency is chosen or avoided for a licensing or advisory reason, which stands on its own; the auditor that would have noticed is not part of it.
  • no-cargo-unmaintained-mentions -- Do not name the unmaintained-crate scanner outside its ignore list and the job that runs it. Its ignore entries need the tool's own heuristics explained; nothing else in the tree does.
  • no-cat-heredoc -- Writing a file by piping a heredoc through cat is banned in this repo. A program embedded that way is invisible to every checker: it is a string as far as the surrounding file is concerned, so nothing lints it, formats it, or reports it in a diff as the language it is written in, and the quoting rules of two layers have to be satisfied before its first byte reaches the interpreter. Put the content in its own file beside whatever runs it -- .mise/ for a mise task body -- named with the extension of whatever reads it (.awk, .jq, .sh), and point the tool at that path instead. It then reads as the program it is, and a path is all the caller needs.
  • no-changelog-links -- CHANGELOG.md must not contain links. What an entry would link to does not exist when it is written: the version tag a compare or release link names is only created after the release merges, so the link 404s -- and fails the link check -- in exactly the change that adds it. Write references as plain text instead: a pull request as #123, a version as the bare [0.3.0] heading, and a project by its name.
  • no-check-task-name-mentions -- Do not name a check or formatter task outside its own config, the images and jobs that run it, and the docs that say when to. Saying code is written a certain way because one of these would object describes the check rather than the code, and it is the first thing to rot: the task gets renamed, folded into an aggregate, or its rule moves, and the comment keeps asserting the old arrangement. State the constraint.
  • no-clang-format-mentions -- Do not name the C formatter outside its own config. Formatted code carries no evidence of who formatted it, and a comment claiming a brace or a blank line is the formatter's doing is unverifiable noise.
  • no-clang-tidy-mentions -- Do not name the C static analyzer outside its config. Its suppression marker is a bare // NOLINT naming the check, which is all a reader needs -- spelling out the analyzer beside it adds nothing and dates the comment to whichever analyzer happened to run that week.
  • no-cmd-scripts -- A .cmd file is banned everywhere in this tree, with no exemption at all -- not in .mise/, and not under a .venv/ the way its sibling extensions are. The reasoning is the same one that bans .bat and .ps1: a Windows-only shell file is exercised on one of five platforms and silently untested on the other four. It gets no carve-out because nothing generates one -- a virtualenv writes activate.bat and activate.ps1 but never a .cmd -- so every .cmd in the tree is something a person wrote by hand, and belongs in a task body under .mise/ or its own directory under utilities/ instead.
  • no-codacy-mentions -- Do not name this analyzer outside its own config and the few files it excludes by path, where the header is the exclusion's canonical home. It runs after a push, so a source comment about it describes something the reader cannot see, run, or check; state the property the code actually has.
  • no-codecov-mentions -- Do not name the coverage service outside the jobs that upload to it. A test exists to pin behaviour, and saying it was added for a coverage percentage tells the next reader to delete it once the number moves.
  • no-comment-in-dev-dependencies -- Do not comment entries in a [dev-dependencies] section. What a test dependency is for belongs in the test that uses it, where the reader is already looking; a note here duplicates that and goes stale the moment the test changes. Delete the comment, and put anything genuinely worth saying next to the code that depends on it.
  • no-conftest-mentions -- Do not name the policy runner outside its own policy tree and the tasks that feed it. A config entry is shaped by what the config has to express; a note that some policy would otherwise object belongs in that policy's own deny message, where it is shown to the person who trips it.
  • no-coverage-task-name-mentions -- Do not name a coverage task outside the pipeline that runs it. Where one is a genuine precondition -- it starts the server a test needs, or it is the trusted caller that makes an argv-derived path safe -- the name is the precondition and belongs there. A test's own assertions never are.
  • no-cpplint-mentions -- Do not name the C++ style checker outside the configs that run it or defer to it. Its suppression marker carries the rule name already, and nothing else in a C file is the checker's business.
  • no-curl-pipe -- Piping curl into another command is banned in this repo. curl ... | sh executes an unreviewed remote script outright: whoever controls the URL, or anyone able to substitute the response, chooses what runs, and a truncated download is indistinguishable from a good one. Download to a file first, verify it with a pinned sha256sum -c or a signature, and only then use it. | tar is banned too, and is not the safe exception it looks like: tar --to-command=<cmd> runs every archive member through that command, and -I / --use-compress-program likewise hand an arbitrary program the stream, so a piped archive can execute code just as a piped script can. Fetch, verify, then extract.
  • no-deepsource-mentions -- Do not name this analyzer outside its own config, the pragmas it reads, and a reference to one of its codes. Naming it in front of a code -- DeepSource RS-W1079 -- is required, because the code alone does not say whose it is. Naming it anywhere else describes the tool's reaction rather than the code.
  • no-dprint-mentions -- Do not name the markdown/YAML/JSON formatter outside its config and the configs held to agree with it. Where a generator has to emit already-formatted output, say which shape the output must have; naming the formatter dates the comment to a config the generator never reads.
  • no-editorconfig-check-mentions -- Do not name the editorconfig checker outside the task that runs it. Write "the editorconfig line length" when a comment genuinely has to mention the limit -- that names the constraint and its source, which is what a reader can act on.
  • no-flawfinder-mentions -- Do not name the C/C++ security scanner except in the flawfinder: ignore pragma, which only works spelled out on the line it covers. Its justification belongs on that same line and says why the call is safe.
  • no-generator-task-name-mentions -- Do not name a generator task outside the generator, the files it writes, and the docs that say when to run it. A generated file's own header is where the name belongs, because a reader who must not hand-edit the file needs the command that rebuilds it. Elsewhere, "the drift check fails" says everything the reader can act on, and survives the task being renamed or folded into an aggregate.
  • no-gitleaks-mentions -- Do not name the secret scanner outside its config and the gitleaks:allow marker it reads. A credential is kept out of a tracked file because it is a credential, not because a scanner would find it.
  • no-hadolint-mentions -- Do not name the Dockerfile linter outside its config, its hadolint ignore= pragmas, and the code that emits them. Say what the instruction does and why it is written that way; a pragma already carries the code it suppresses.
  • no-jscpd-mentions -- Do not name the duplication checker outside its config, its baseline, and the policy that ratchets them. A reported clone is a request to remove duplication: factor the shared part out so it exists once. Where a clone is genuinely irreducible -- the same few lines every caller must write verbatim -- leave the code plain and raise it, rather than annotating the source with the tool's name.
  • no-kubeconform-mentions -- Do not name the manifest schema validator outside the job that runs it. A manifest field is set because Kubernetes requires or honours it, which is what the comment beside it should say.
  • no-line-length-in-comment -- Do not hardcode the line-length number in a comment. Say "editorconfig line length" when the comment genuinely needs the constraint, otherwise drop the mention entirely -- the number drifts and adds noise.
  • no-ls-lint-mentions -- Do not name the file-naming linter outside its config and the rule that governs it. A file or directory is named for what it holds; the checker that would object to another name is not part of that.
  • no-lychee-mentions -- Do not name the link checker outside its config and the task that runs it. A link is written to point somewhere useful, and a note that the checker accepts or skips it will outlive the reason it did.
  • no-maintainer-task-name-mentions -- Do not name an install, prefetch or asset-publishing task outside the jobs, images and asset tables that invoke it. These run on a maintainer's machine or a runner, never from the code, so a source file naming one is describing an operation it has no part in.
  • no-nextest-mentions -- Do not name the test runner outside its config and the jobs that invoke it. A test asserts what the code must do, and how it happens to be scheduled is the runner's business rather than the test's -- unless the schedule is load-bearing, which is a constraint worth stating and worth allowlisting for.
  • no-non-ascii -- Non-ASCII character. Keep source, config, docs and generated output ASCII-only: use -- for an em-dash, -> for an arrow, ... for an ellipsis, <=/!=/^2 for math glyphs, and straight quotes for curly quotes. ASCII keeps terminals, log scrapers, and grep reading the same bytes the editor shows. Nothing is exempt: when a generator emits the character, fix the generator or the task that runs it.
  • no-observability-task-name-mentions -- Do not name an observability task outside the configs it loads and the things that launch it. A collector config naming the task that reads it is how a reader finds its way in; a comment elsewhere saying some value exists for one of these tasks is not.
  • no-os-scope-in-platform-mise-config -- Do not set os = [...] in a platform-specific mise config. The file is only loaded on its own platform, so the key restates what the filename already says, and a value naming a different OS silently disables the entry everywhere. Drop the key; a tool that genuinely needs scoping to a subset of platforms belongs in a cross-platform config instead.
  • no-osv-scanner-mentions -- Do not name the vulnerability scanner outside its config, the job that runs it, and the ignore lists that have to say which scanner honours which id. Everywhere else, describe the dependency, not the scan.
  • no-oxfmt-mentions -- Do not name the JS formatter outside its config and the config held to agree with it. When a statement has to be split to keep both formatters happy, the fix is in the source -- a shorter line, an extracted local -- and the split stands on its own without a comment naming either tool.
  • no-oxlint-mentions -- Do not name the JS linter outside its config. A skipcq or eslint-style disable comment already carries the rule it silences plus the reason it is safe; adding the linter's name tells the reader nothing more.
  • no-pyrefly-mentions -- Do not name the Python type checker outside its config and the rules that protect what it can see. An annotation states the type the code requires; that it also satisfies a checker follows, and need not be written down.
  • no-python-outside-modules -- Python outside the module trees is banned. It is a guest language in this repo, not a tooling language: it is here so that a module can be written in it and run in a browser or a runner, and every such file sits under modules/ beside the manifest that builds and packages it. A .py anywhere else is a loose script by another name, carrying every problem one brings -- nothing invokes it, nothing tests it, and nothing fails when it stops working. Short and simple belongs in a task body under .mise/; anything more involved gets its own directory under utilities/ with a README.md.
  • no-regal-mentions -- Do not name the Rego linter outside its own config. A policy is written the way Rego reads best; if one of its idioms is deliberate, say what the idiom does rather than which linter asked for it.
  • no-repo-mentions-in-verification -- A scenario's generated docs must not mention git or a checkout. They are read by whoever deploys the scenario, on machines that hold the generated directory and nothing else, so an instruction that assumes the source repository is there is one that reader cannot follow. Change the et-cli text that renders it.
  • no-roslynator-mentions -- Do not name the C# analyzer outside the tasks and images that install it. A #pragma warning disable already names the diagnostic, which is the part a reader can look up.
  • no-ruff-mentions -- Do not name the Python linter/formatter outside its config and the configs that defer to it. A # noqa carries the code it silences and should carry the reason too; the tool that issued the code does not need naming beside it.
  • no-ryl-mentions -- Do not name the YAML linter outside its own config. YAML is written to be read; where its shape is forced, say what forces it, which is almost always the schema the file answers to rather than a linter.
  • no-scripts-dir -- A scripts/ directory is banned in this repo. Every script belongs in one of exactly two places, and neither of them is a loose tree of files that nothing points at. Short and simple goes inline in a task body under .mise/, where it is listed alongside every other entry point and runs by name. Anything more involved gets its own directory under utilities/, with a README.md saying what it does and how to run it. A file under scripts/ has neither: no caller, no documentation, and nothing that fails when it stops working. Move it to whichever of the two fits, and drop the directory.
  • no-semgrep-mentions -- Do not name this tool outside its own ruleset and the configs that scope it. A nosemgrep marker is the one place the name belongs in source, and it needs the rule id and a reason beside it; anywhere else, pointing at a rule duplicates the message that rule already shows whoever trips it.
  • no-shell-scripts -- A .sh file outside .mise/ is banned. Shell that is short and simple belongs inline in a task body, where it is discoverable and runs by name; shell that has outgrown that is a program, and belongs in its own directory under utilities/, written in a language with types, tests and a linter behind it. The one exception is .mise/, where a task body too long to sit inside a TOML string is split into a file beside the config that invokes it, named for the interpreter that reads it -- that file is formatted and linted as the shell it is, which a string embedded in a config never can be.
  • no-shellcheck-mentions -- Do not name the shell linter except in the # shellcheck disable= pragma, which only works spelled out above the line it covers, and in the configs that run it. The pragma needs the code and the reason the construct is deliberate; nothing else in the script does.
  • no-shfmt-mentions -- Do not name the shell formatter outside the tasks that run it. Formatted shell carries no evidence of who formatted it, so a comment claiming an indent is the formatter's doing cannot be checked.
  • no-split-url-in-comment -- A URL is broken across two comment lines, which leaves a dead link. Rejoin it onto one line, even if that line then runs past the usual width, and re-wrap the surrounding prose around it. A comment reflow that split it cannot be told to keep a URL whole, so the repair is always by hand.
  • no-suppressing-unexemptable-rules (meta) -- This suppression comment exempts the line from a rule the repository holds to every file without exception, either by naming that rule or by naming no rule at all and so silencing every one. Fix the line instead; when a generator wrote it, fix the generator or the task that runs it. A marker that has to stay names the single rule it silences and the reason beside it.
  • no-taplo-mentions -- Do not name the TOML formatter and schema linter outside its config and schemas. Where a generator has to emit TOML the formatter will accept unchanged, say which shape the output must have -- sorted arrays, aligned comments -- rather than naming the tool that would otherwise rewrite it.
  • no-test-task-name-mentions -- Do not name a test or server task outside the jobs and docs that run it. A test file may name the task that supplies something it cannot create for itself -- a browser harness, a listening backend, a port group -- because that is a precondition. Naming one to explain why an assertion exists is not.
  • no-todo -- TODOs are not allowed in this repo. Either resolve the work now, or, if it must defer, add a single-line justification in the surrounding code and add this file's path to no-todo.yaml's paths.exclude allowlist. Do not duplicate a TODO across multiple files -- pick the single canonical home and let grep TODO find it there.
  • no-trailing-backslash -- Trailing-backslash line continuations are not allowed in this repo. Keep it on one line, or use a language-appropriate alternative (concat!(...) in Rust, a mise [vars] entry, a YAML/array form). If a file genuinely needs them, add it to this rule's paths.exclude allowlist.
  • no-trivy-mentions -- Do not name the container and manifest scanner outside its ignore file and the task that runs it. Its ignore entries need its own resolution behaviour explained; nothing it scans does.
  • no-type-comments -- Legacy # type: comment. Use an inline annotation (x: T = ..., def f(a: T) -> R:) instead. The compiled Pyrefly type checker that backs check:python parses annotations but silently ignores legacy type comments, so a broken one (an undefined name, a stray --) rots unseen -- exactly DeepSource TYP-025. Annotations are the only form both tools check. # type: ignore (the suppression pragma) is exempt.
  • no-typos-check-mentions -- Do not name the spelling checker's tasks outside the config that defines them. An identifier spelled an unusual way is either correct or a typo; if it is correct, say what it means, and add it to the checker's own allowlist rather than explaining it where it appears.
  • no-wget -- Use curl instead of wget for downloads in this repo. Two download tools means two sets of flags, two retry behaviours and two failure modes for one job, and the difference is easy to get wrong: wget writes a file by default while curl writes to stdout unless told otherwise, so a command translated between them silently changes what it produces. curl is also the one already installed -- it is in the Dockerfiles' COMMON_PACKAGES and present on the CI runners, whereas wget is in none of the image package lists, so reaching for it adds an undeclared dependency on whatever the host happens to ship. Note that piping a download into an interpreter stays banned whichever tool fetches it.
  • no-windows-shell-scripts -- A .bat or .ps1 file is banned. Windows is a first-tier platform here, which is precisely why its shell gets no scripts of its own: a task body runs the same way on all five supported platforms, so logic written once is logic every lane exercises, whereas a batch or PowerShell file is reachable from one platform and silently untested on the other four. That is how a Windows-only path rots unnoticed until it is the lane holding up a merge. Write the logic once in a task body under .mise/, or in its own directory under utilities/ when it has outgrown one, and let every platform run the same thing. The .venv/ exemption covers the activation scripts a virtualenv generates for itself, which are the tool's output rather than anyone's source.
  • no-zizmor-mentions -- Do not name the Actions security auditor except in the zizmor: ignore[...] pragma, which only works spelled out, and in the configs that run it. A workflow step is written to be safe; say why it is, and let the pragma carry the audit it waives.
  • prefer-yaml-extension -- Use the .yaml extension, not .yml -- rename the file.
  • prefer-yaml-toml-over-json -- Prefer YAML or TOML over JSON/JSONC/JSONL for config. If the tool requires a JSON-family format, prefer .jsonc over .json so the config can carry inline comments, and add the file to this rule's paths.exclude allowlist.
  • published-from-registry -- A published scenario image may be built only on an image from docker.io or ghcr.io, and has to name that registry. These are the deployments generated against released artifacts, so every base they build on is something the operator pulls on trust; holding each FROM to the two registries this project publishes to and draws its upstream images from, spelled out rather than left to the daemon's default, keeps an image from anywhere else -- or a build context standing in for one -- from becoming part of a deployment unseen. A flag such as --platform is refused the same way, since it is not a registry.
  • published-from-stage-only -- In a published scenario image, --from= may name only one of the Dockerfile's own stages, released or deps. COPY --from= and RUN --mount=...,from= both take an image reference as readily as a stage name, and a bare name that matches no stage is pulled from a registry -- so either is a way to bring an image into the build that no FROM line declares and the rule holding every FROM to docker.io and ghcr.io never sees. The stage names are the ones et-cli generates.
  • published-no-additional-contexts -- A published deployment hands its image build no additional context, neither a compose additional_contexts: nor a docker build --build-context. A named context substitutes whatever it points at -- a local image, a directory, another service's build -- for the name a FROM or COPY --from= uses, so it is a way round the rules holding a published Dockerfile to registry images it declares itself. A published scenario image names the released hub directly and fetches its modules from their releases, so it has nothing to be handed.
  • published-no-relative-path -- A published deployment names no relative path, so neither .. nor ./ has a place in one. These are the deployments generated against released artifacts, for someone who has the releases and not this repository. A path climbing out of the deployment directory is a path into a checkout they do not have -- a compose build context of ../../../.. reads this repository's tree, which only works by accident from inside it -- and a ./ path is relative to wherever a command happens to run rather than to anything the deployment fixes. Fetch what the deployment needs from its releases, and let each tool's own default stand for "this directory", so the deployment builds and runs from its directory alone.
  • python-silent-broad-except -- Silently discarding every ordinary exception can conceal programming errors and lost work. Catch the expected exception types and explicitly report, recover from, or propagate the failure. Check-only: a safe recovery policy cannot be inferred from the handler alone.
  • rego-rule-summary (meta) -- This Rego rule has no summary comment. The generated catalogue of every check lists each policy rule by the first line of the comment above it, and refuses to build without one, so describe what the rule enforces in a comment opening at column 0 between the previous rule's closing brace and this rule. The file's header comment does not count: it sits above the package line and describes the policy, not any one rule in it.
  • single-key-env-subtable-should-be-inline -- [tasks.X.env] subtable has a single KEY = "value" assignment. Inline it on the parent task as env = { KEY = "value" } when the resulting line fits the editorconfig line length -- subtable form is reserved for multi-key envs.
  • single-line-description-in-mise-config -- mise task description fields must be single-line "..." strings, not multi-line """...""". Keep the description short enough to fit on one line.
  • sorted-lists -- Keep this list in ASCII order of each item as written: the traits in a #[derive(...)] (Clone, Debug, Deserialize, Eq, PartialEq, Serialize, a qualified strum::EnumIter after every bare name), the lints in an #[expect(...)] before its reason (clippy:: lints together, rustc lints after them), and adjacent feature = "..." predicates in an any(...) / all(...). A sorted list reads the same everywhere, makes a missing or duplicated entry easy to spot, and keeps a diff to the one entry that changed.
  • task-shell-must-use-task-shell-var -- Declare a task shell through one of the task_shell vars, never as a bare bash ... command line. The vars run the body through a wrapper script instead of -c, which is what repairs the mangled PATH mise hands a Windows task; a raw bash ... -c bypasses the wrapper and every tool goes missing on the Windows lanes. Use {{ vars.task_shell }}, or task_shell_trace for xtrace, or task_shell_plain for a body that checks its own exit codes.

shellcheck

  • shellcheck-check (default env; run by check:rust) -- shellcheck the standalone shell fragments committed under .mise/.
  • shellcheck-mise-check (default env; run by check:rust) -- shellcheck every bash-shell mise task body across .mise/config*.toml. Config: .mise/shellcheck-mise.jq.

taplo

  • taplo-check (default env; run by check:rust). Config: config/taplo.toml, config/taplo/mise-cargo-backend-allowlist.schema.json, config/taplo/mise-no-ubi-backend.schema.json, config/taplo/mise-run-not-array.schema.json, config/taplo/no-banned-deps.schema.json, config/taplo/no-lib-name.schema.json, config/taplo/no-path-deps.schema.json, config/taplo/no-wildcard-or-git-deps.schema.json, config/taplo/require-lib-doctest-false.schema.json, config/taplo/require-lints-section.schema.json, config/taplo/require-workspace-deps.schema.json.
  • taplo-fmt-check (default env; run by check:rust). Repaired by taplo-fmt. Config: config/taplo.toml.

Rules, from config/taplo:

  • mise-cargo-backend-allowlist -- mise [tools] -- cargo backend allowlist: cargo: source build; tools with no prebuilt + os-scoped fallbacks (findutils, ryl).
  • mise-no-ubi-backend -- mise [tools] -- no ubi backend: The ubi: backend is deprecated; use http: instead.
  • mise-run-not-array -- mise tasks -- run must be a string, not an array: run must be a string, not an array: taplo reorder_arrays would re-sort the commands.
  • no-banned-deps -- Cargo.toml -- banned deps: Crates this repo deliberately doesn't take on; const = rejection message.
  • no-lib-name -- Workspace member Cargo.toml -- [lib] must not override the crate name: The library name must default from the package name; an explicit [lib] name rename is banned.
  • no-path-deps -- Workspace member Cargo.toml -- no path dependencies: Forbids path = ... deps outside workspace root; const is a hack so taplo prints it as the error.
  • no-wildcard-or-git-deps -- Workspace member Cargo.toml -- no wildcard or git deps: Forbids * and inline git = ... in [dependencies] / [dev-dependencies] / [build-dependencies].
  • require-lib-doctest-false -- Workspace member Cargo.toml -- [lib] must set doctest = false: Pairs with the ban on fenced blocks in doc comments; runnable examples go in tests/.
  • require-lints-section -- Workspace member Cargo.toml -- must inherit workspace lints: Member crates must include [lints] workspace = true to inherit workspace lints.
  • require-workspace-deps -- Workspace member Cargo.toml -- deps must inherit: [dependencies] / [dev-dependencies] / [build-dependencies] entries must use .workspace = true.

trivy

  • trivy-check (default env; run by check:rust) -- Check every Dockerfile and generated Kubernetes manifest for hardening findings. Config: config/trivyignore.yaml.

typos

  • typos-check (default env; run by check:rust). Repaired by typos-fix. Config: config/typos.toml.

xcrun

  • macos-sdk-check (default env; not run by mise run check) -- Fail if the macOS SDK is missing or older than the minimum this repo builds against.

zig

  • zig-check (zig env; run by check:zig). Repaired by zig-fmt.

zizmor

  • zizmor-check (default env; run by check:rust) -- Audit GitHub Actions workflows + composite actions for security issues (zizmor). Config: config/zizmor.yaml.

Shell-script checks

  • action-validator-check (windows env; run by check:rust).
  • agent-lint-check (windows env; run by check:rust).
  • branch-up-to-date-check (default env; run by check:rust) -- Fail if the current branch does not already contain origin/main (rebase needed).
  • check-ghcr-visibility (maint env; not run by mise run check) -- Verify every GHCR package is public; print settings URLs for any still private.
  • code-scanning-check (default env; not run by mise run check) -- Fail if the GitHub repository has any open code-scanning alert.
  • dockerignore-check (default env; run by check:rust) -- Fail if .dockerignore is stale vs .gitignore (run mise run gen:dockerignore).
  • gen-specs-check (default env; run by check:rust) -- Fail if any checked-in et-int-gen artifact is stale.
  • mise-lock-check (default env; run by check:rust) -- Fail if .mise/mise*.lock or .mise/locks/ differ from what a full install resolves.
  • readme-mise-version-check (default env; run by check:rust) -- Fail if README.md's mise version differs from min_version in .mise/config.toml.
  • verification-check (default env; run by check:rust) -- Fail if generated scenario output files are stale.