Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .mise/config.python.toml
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ run = """
shell = "{{ vars.task_shell }}"

[tasks.build-ws-pymath1-module]
depends = ["build-et-cli"]
# Its shim starts Pyodide through edge-toolkit's, which has to be built to be served.
depends = ["build-edge-toolkit-wheel"]
description = "Build the pymath1 FedAvg module"
dir = "modules/web/pymath1"
run = """
Expand All @@ -200,7 +201,7 @@ run = """
shell = "{{ vars.task_shell }}"

[tasks.build-ws-pydata1-module]
depends = ["build-et-rest-client-wheel"]
depends = ["build-edge-toolkit-wheel", "build-et-rest-client-wheel"]
description = "Build the pydata1 Python workflow module"
dir = "modules/web/pydata1"
# et-rest-client is its own ws-module mounted at /modules/et-rest-client/.
Expand Down
7 changes: 4 additions & 3 deletions .mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ ripgrep = "latest"
# Upstream ships prebuilt tarballs for every tier-1 and tier-2 triple, so the github (ubi) backend covers all five
# platforms with no os-scoping. Clone fingerprints are tokenizer-derived, so a new jscpd release can rewrite them
# and redden config/jscpd-baseline.json; the lockfile holds the version until a refresh moves it. When it moves,
# regenerate the baseline and set `declared_clones` in the jscpd conftest policy to its new count in the same change.
# regenerate the baseline and, in the same change, set `declared_clones` in the jscpd conftest policy to the smallest
# multiple of its `ceiling_step` at or above the new count.
"github:kucherenko/jscpd" = "latest"
# ONNX Runtime prebuilt -- ort-sys 2.0.0-rc.10 binds to ORT 1.22.x.
"cargo:ryl" = { version = "latest", os = ["macos/x64"] }
Expand Down Expand Up @@ -3353,8 +3354,8 @@ shell = "{{ vars.task_shell }}"

[tasks.fetch-llm1-rclone]
# llm1's SmolLM2 weights, tokenizer and configs, fetched straight from HuggingFace.
# The five files land under the model module's gitignored pkg/, which et-modules-service then serves as
# /modules/et-model-llm1/... -- exactly the layout transformers.js expects of a local model. Deliberately not
# The five files land under the model module's gitignored pkg/, which et-modules-service then serves under the
# module's own scoped path -- exactly the layout transformers.js expects of a local model. Deliberately not
# in prefetch-ci: at ~118 MB the weights are a developer-side download, and only llm1 reads them.
run = """
opts="--http-url https://huggingface.co --progress --ignore-existing"
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1320,9 +1320,9 @@ report the added/removed counts the update task prints so the delta is reviewabl
Upgrading jscpd itself is the one rewrite an agent may make without asking. A new release re-tokenizes the same
code -- new languages, a changed tokenizer, different line counting -- so its fingerprints move although no
duplication was added. In the change that bumps the jscpd pin, run `jscpd-baseline-update`, set `declared_clones`
in `config/conftest/policy/jscpd/jscpd.rego` to the regenerated baseline's fingerprint count, and report the
added/removed counts. The bump must be the only thing that moves the baseline: duplication the same change
introduces is still yours to factor out.
in `config/conftest/policy/jscpd/jscpd.rego` to the smallest multiple of its `ceiling_step` at or above the
regenerated baseline's fingerprint count, and report the added/removed counts. The bump must be the only thing that
moves the baseline: duplication the same change introduces is still yours to factor out.

### A finding from an external analyzer must end up in a local checker

Expand Down
26 changes: 23 additions & 3 deletions CHECKS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Checks

- **Generated:** 2026-11-07
- **Generated:** 2026-11-08
- **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
- **Rules:** 374 custom local rules, or non-default strict linter settings
- **Meta rules:** 21 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
Expand Down Expand Up @@ -107,6 +107,11 @@ Rules, from `config/ast-grep/rules`:
- `inherent-impl-first` -- Put a type's inherent `impl` block before its trait `impl`s 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.
- `js-no-function-declaration-in-script` -- A top-level `function` declaration in a file with no `import` or `export`
(`DeepSource JS-0067`). Such a file parses as a classic script, where the declaration becomes a property of the global
object shared with every other script, and the external analyzer reads it that way even when the file is loaded as a
module worker. Bind the function with `const` instead. Check-only: an arrow function is not hoisted and has no `this`
or `arguments` of its own, so the rewrite can change behaviour and needs reading per site.
- `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
Expand Down Expand Up @@ -150,6 +155,9 @@ Rules, from `config/ast-grep/rules`:
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-empty-new-on-default-type` -- An empty `new()` on a type that implements `Default` (`DeepSource RS-W1079`):
construct it with `default()`, which says plainly that the value is the type's zero value rather than something `new`
sets up. `String` has a rule of its own; this one covers `PathBuf` and every type the same file derives `Default` for.
- `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,
Expand Down Expand Up @@ -303,10 +311,21 @@ Rules, from `config/ast-grep/rules`:
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-next-without-default` -- `next()` with no default raises `StopIteration` when the iterator is empty
(`DeepSource PTC-W0063`). Inside a generator that ends the generator silently, and anywhere else it surfaces as an
exception that names nothing about what was missing. Pass a default and handle it, or unpack the match (`(item,) =
[...]`) so an absent or repeated match fails with a `ValueError` saying so. Check-only: the right default, and what to
do when it comes back, depends on the call site.
- `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.
- `python-staticmethod-overrides-instance-method` -- This `@staticmethod` overrides a method its base class defines as
an instance method (pylint `W0221`, pyright `reportIncompatibleMethodOverride`). The base takes `self` and the
override takes nothing, so a call through the class, `Base.method(obj)`, breaks on the subclass, and the repo's own
type checker does not report the mismatch. Make the base method static too when neither uses `self`, or keep the
override an instance method. Only a base class defined in the same file is caught, since one imported from elsewhere
is out of sight. Check-only: which side to change depends on whether the base needs `self`.
- `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
Expand Down Expand Up @@ -701,6 +720,7 @@ Rules, from `config/conftest/policy`:
- `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.
- declared_clones sits on a multiple of ceiling_step, whichever way it last moved.
- `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 }}"`.
Expand Down
31 changes: 31 additions & 0 deletions config/ast-grep/rules/js-no-function-declaration-in-script.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
id: js-no-function-declaration-in-script
language: JavaScript
severity: error
message: |
A top-level `function` declaration in a file with no `import` or `export` (`DeepSource JS-0067`). Such a file parses
as a classic script, where the declaration becomes a property of the global object shared with every other script,
and the external analyzer reads it that way even when the file is loaded as a module worker. Bind the function with
`const` instead. Check-only: an arrow function is not hoisted and has no `this` or `arguments` of its own, so the
rewrite can change behaviour and needs reading per site.
rule:
any:
- kind: function_declaration
- kind: generator_function_declaration
inside:
kind: program
not:
has:
any:
- kind: import_statement
- kind: export_statement
# A top-level `await` parses only in a module, so the file cannot be a classic script.
- kind: expression_statement
has:
kind: await_expression
- kind: lexical_declaration
has:
kind: variable_declarator
has:
kind: await_expression
ignores:
- generated/**
37 changes: 37 additions & 0 deletions config/ast-grep/rules/no-empty-new-on-default-type.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
id: no-empty-new-on-default-type
language: Rust
severity: error
message: |
An empty `new()` on a type that implements `Default` (`DeepSource RS-W1079`): construct it with `default()`, which
says plainly that the value is the type's zero value rather than something `new` sets up. `String` has a rule of
its own; this one covers `PathBuf` and every type the same file derives `Default` for.
# A type counts as deriving `Default` only when this file derives it.
# The derive has to sit in the run of attributes directly above the type's `struct`; a `Default` implemented by
# hand, or derived in another file, is out of reach of a single-file rule.
rule:
pattern: $TYPE::new()

Check warning on line 12 in config/ast-grep/rules/no-empty-new-on-default-type.yaml

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/ast-grep/rules/no-empty-new-on-default-type.yaml#L12

Using a single metavariable as a pattern drastically slows down the rule performance because it will match every expression in a file.
any:
- has:
field: function
regex: "^(std::path::)?PathBuf::new$"
- inside:
kind: source_file
stopBy: end
has:
kind: struct_item
has:
field: name
pattern: $TYPE

Check warning on line 24 in config/ast-grep/rules/no-empty-new-on-default-type.yaml

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/ast-grep/rules/no-empty-new-on-default-type.yaml#L24

Using a single metavariable as a pattern drastically slows down the rule performance because it will match every expression in a file.
follows:
kind: attribute_item
regex: "^#\\[derive\\([^)]*\\bDefault\\b"
stopBy:
not:
any:
- kind: attribute_item
- kind: line_comment
# Mechanical autofix, since the rule only fires where `default()` exists.
# An empty `new()` beside a derived `Default` that built anything else would be the bug worth seeing in the diff.
fix: $TYPE::default()
ignores:
- generated/**
25 changes: 25 additions & 0 deletions config/ast-grep/rules/python-next-without-default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
id: python-next-without-default
language: Python
severity: error
message: |
`next()` with no default raises `StopIteration` when the iterator is empty (`DeepSource PTC-W0063`). Inside a
generator that ends the generator silently, and anywhere else it surfaces as an exception that names nothing about
what was missing. Pass a default and handle it, or unpack the match (`(item,) = [...]`) so an absent or repeated
match fails with a `ValueError` saying so. Check-only: the right default, and what to do when it comes back,
depends on the call site.
rule:
any:
- pattern: next($ITERATOR)
# `next(x for x in xs)` parses with the bare generator expression as the call's whole argument node.
- kind: call
all:
- has:
field: function
regex: "^next$"
- has:
field: arguments
kind: generator_expression
ignores:
- generated/**
- "**/componentize_py_async_support/**"
- "**/wit_world/**"
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
id: python-staticmethod-overrides-instance-method
language: Python
severity: error
message: |
This `@staticmethod` overrides a method its base class defines as an instance method (pylint `W0221`, pyright
`reportIncompatibleMethodOverride`). The base takes `self` and the override takes nothing, so a call through the
class, `Base.method(obj)`, breaks on the subclass, and the repo's own type checker does not report the mismatch.
Make the base method static too when neither uses `self`, or keep the override an instance method. Only a base
class defined in the same file is caught, since one imported from elsewhere is out of sight. Check-only: which
side to change depends on whether the base needs `self`.
rule:
kind: decorated_definition
all:
- has:
kind: decorator
regex: "^@staticmethod$"
- has:
field: definition
kind: function_definition
has:
field: name
pattern: $METHOD

Check warning on line 22 in config/ast-grep/rules/python-staticmethod-overrides-instance-method.yaml

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/ast-grep/rules/python-staticmethod-overrides-instance-method.yaml#L22

Using a single metavariable as a pattern drastically slows down the rule performance because it will match every expression in a file.
# The class whose body holds the override, and one base it names.
- inside:
kind: block
inside:
kind: class_definition
has:
field: superclasses
has:
kind: identifier
pattern: $BASE

Check warning on line 32 in config/ast-grep/rules/python-staticmethod-overrides-instance-method.yaml

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/ast-grep/rules/python-staticmethod-overrides-instance-method.yaml#L32

Using a single metavariable as a pattern drastically slows down the rule performance because it will match every expression in a file.
# That base, defined at the top of the same file, with an undecorated method of the same name.
- inside:
kind: module
stopBy: end
has:
kind: class_definition
all:
- has:
field: name
pattern: $BASE
- has:
field: body
has:
kind: function_definition
has:
field: name
pattern: $METHOD
ignores:
- generated/**
- "**/componentize_py_async_support/**"
- "**/wit_world/**"
41 changes: 41 additions & 0 deletions config/ast-grep/tests/js-no-function-declaration-in-script.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
id: js-no-function-declaration-in-script
valid:
- |
const call = (type) => type;
- |
import { helper } from "./helper.js";
function local() {
return helper();
}
- |
export function run() {}
function appendOutput(msg) {
console.log(msg);
}
- |
const outer = () => {
function inner() {}
return inner;
};
- |
const { run } = await import("./module.js");
function helper() {}
- |
await new Promise((resolve) => setTimeout(resolve, 0));
function helper() {}
invalid:
- |
function call(type) {
return type;
}
- |
async function load() {}
- |
function* frames() {
yield 1;
}
- |
const load = async () => {
await fetch("x");
};
function helper() {}
61 changes: 61 additions & 0 deletions config/ast-grep/tests/no-empty-new-on-default-type.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
id: no-empty-new-on-default-type
valid:
- |
fn build() -> PathBuf {
PathBuf::default()
}
- |
fn build() -> Vec<u8> {
Vec::new()
}
- |
struct Sensors {
active: bool,
}
fn build() -> Sensors {
Sensors::new()
}
- |
#[derive(Default)]
struct Other;
struct Sensors;
fn build() -> Sensors {
Sensors::new()
}
- |
#[derive(Default)]
struct Sensors;
fn build() -> Sensors {
Sensors::new_with(1)
}
invalid:
- |
fn build() -> PathBuf {
PathBuf::new()
}
- |
fn build() -> std::path::PathBuf {
std::path::PathBuf::new()
}
- |
#[derive(Default)]
struct Sensors;
fn build() -> Sensors {
Sensors::new()
}
- |
#[wasm_bindgen] #[derive(Clone, Default)]
pub struct Sensors {
active: bool,
}
fn build() -> Sensors {
Sensors::new()
}
- |
#[derive(Default)]
// A comment between the attributes and the struct.
#[wasm_bindgen]
pub struct Sensors;
fn build() -> Sensors {
Sensors::new()
}
13 changes: 13 additions & 0 deletions config/ast-grep/tests/python-next-without-default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
id: python-next-without-default
valid:
- |
right = next((iris for iris in irises if iris["label"] == "right_eye"), None)
- |
(right,) = [iris for iris in irises if iris["label"] == "right_eye"]
- |
value = iterator.next()
invalid:
- |
right = next(iris for iris in irises if iris["label"] == "right_eye")
- |
first = next(iter(values))
Loading
Loading