From ae7fef5afc921732d248cf22ebae457ba97b2ee7 Mon Sep 17 00:00:00 2001 From: Exploit Intel Date: Tue, 18 Aug 2026 17:11:25 -0400 Subject: [PATCH 1/2] Type-check this package as a ratchet Nothing type-checked this code. mypy finds 22 errors across 7 of 14 modules. Everything is checked by default and the 7 failing modules are listed as exemptions, never the inverse. Listing what is checked would leave every new module unchecked by default, so debt would accumulate silently and the list would be the same size in a year. This way new code is checked without anyone deciding to check it, the list reads as a debt register rather than an achievement, and it can only shrink. The errors are narrowing failures in query, cursor and rendering paths. Fixing them belongs in deliberate per-module changes with their own test evidence, not bundled into a tooling PR where a mistake would be invisible. Removing a module from the list plus fixing its errors is the unit of progress. warn_unused_configs reports an exemption that matches nothing, but mypy prints that as a note and still exits 0, so the CI step pipes the output and fails on it. Without that a fixed module could quietly keep its exemption. Verified rather than assumed: the current tree passes, and a new module with a deliberate type error fails, confirming new code is checked. Behaviour unchanged. --- .github/workflows/quality.yml | 9 +++++++++ .gitignore | 2 ++ pyproject.toml | 32 ++++++++++++++++++++++++++++++++ requirements-dev.txt | 1 + 4 files changed, 44 insertions(+) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 7f6149b..ea4b97b 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -42,6 +42,15 @@ jobs: - name: Ruff format run: ruff format --check src tests + - name: Mypy + run: | + set -o pipefail + mypy 2>&1 | tee mypy.log + if grep -q 'unused section' mypy.log; then + echo "::error::A mypy exemption in pyproject.toml matches nothing. Remove it." + exit 1 + fi + - name: Pytest run: >- python -m pytest -q diff --git a/.gitignore b/.gitignore index f95354e..dafcdd1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ build/ *.egg-info/ .env .env.local +.mypy_cache/ +mypy.log diff --git a/pyproject.toml b/pyproject.toml index 16ced71..52eb720 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,38 @@ eip-search = "eip_search_v3.cli:entrypoint" [tool.setuptools.packages.find] where = ["src"] +[tool.mypy] +python_version = "3.12" +files = ["src"] +ignore_missing_imports = true +# Report an override that matches nothing, so an exemption cannot outlive the +# errors it was added for. mypy prints that as a note and still exits 0, so the +# CI step greps for it and fails. +warn_unused_configs = true + +# Type checking is a ratchet, not an allowlist. Everything is checked by default +# and the exemptions are listed, never the inverse. Listing what IS checked would +# leave every new module unchecked by default, so debt would accumulate silently +# and the list would be the same size in a year. This way new code is checked +# without anyone deciding to check it, and the list below can only shrink. +# +# 7 of 14 modules are exempt, holding 22 errors: cli, client, config, render.common, render.exploit, render.labs, render.vulnerability. +# Most are one shape: a corpus lookup that may return None, then read with .get +# in the renderers. Fixing that properly means giving the lookup a precise return +# type, which is worth doing deliberately rather than inside a tooling change. +# Removing a module from this list plus fixing its errors is the unit of progress. +[[tool.mypy.overrides]] +module = [ + "eip_search_v3.cli", + "eip_search_v3.client", + "eip_search_v3.config", + "eip_search_v3.render.common", + "eip_search_v3.render.exploit", + "eip_search_v3.render.labs", + "eip_search_v3.render.vulnerability", +] +ignore_errors = true + [tool.ruff] # Pin the formatter to the version CI installs. `ruff check` findings are stable # across releases; `ruff format` output is not, and AGENTS.md tells a maintainer diff --git a/requirements-dev.txt b/requirements-dev.txt index 45887f1..8ac1883 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,3 +3,4 @@ twine>=6,<7 pytest==8.4.2 pytest-cov>=5 ruff==0.16.3 +mypy==2.3.1 From bc1a2c31adf1034e10e65cbfdc38664a98d54e37 Mon Sep 17 00:00:00 2001 From: Exploit Intel Date: Tue, 18 Aug 2026 17:15:47 -0400 Subject: [PATCH 2/2] Say what CI actually runs The verification block listed ruff check and pytest. CI now also runs ruff format --check and mypy, added by this campaign, so the list was two gates short of the truth. --- AGENTS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index d095ca7..93b2468 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,6 +40,8 @@ python3 -m venv --clear .venv # --clear: a uv-made .venv has no pip python -m pip install -r requirements-dev.txt python -m pip install -e . ruff check src tests +ruff format --check src tests +mypy pytest -q -m 'not live' --cov=eip_search_v3 --cov-fail-under=90 ! git grep --untracked -n -I -P '[\x{2013}\x{2014}]' -- . # CI rejects en/em dashes ```