Type-checking migration strategy
BreachPilot is migrating toward strict mypy incrementally. A whole-repo strict conversion in one PR would be reckless (hundreds of errors across unrelated subsystems); instead, existing debt is tolerated but new debt is rejected.
The three gates (CI types job)
- Permissive check —
mypy --follow-imports=skip toolswith thedisable_error_codesuppressions inpyproject.toml. Must pass with zero errors. This is the primary gate: no new unsuppressed errors. - Debt gate —
python scripts/mypy_debt.pyruns mypy with zero suppressions (hermetic temp config, fixedpython_version = 3.12) and compares againstmypy-baseline.txt(total + per-file counts). Fails on any increase — a higher total, a higher per-file count, or errors in a previously clean file. Paying debt down always passes. - Strict subsystems — per-module overrides in
pyproject.tomlwithdisable_error_code = []plus the fullenable_error_codelist. Currently strict:tools.validation_utils,tools.exceptions,tools.mcp_shared,tools.kernel.*,tools.sandbox.*.
Graduation order
tools/kernel— done (2026-09-07).tools/api— next.run_manager.pyholds 35 errors, nearly allunion-attronhandle.event_broker(RunEventBroker | None); fixing them means deciding the None-contract at each emit site, not sprinkling asserts.tools/sandbox— done (2026-09-07).- Orchestration/session code (
run_service/,mcp_session.py,campaign/,swarm/) — largest remaining clusters (run_service/execute.py,mcp_session.py,attack_ui.py). - Remaining modules, highest-count first (
mypy-baseline.txtis sorted for triage; error codes are dominated byunion-attr,attr-defined,name-defined).
How to graduate a module
- Fix its errors with real types —
TypedDict, dataclasses,Protocol, generics, explicitOptional+Nonenarrowing. Do not silence with bareAny, per-fileignore_errors, or newdisable_error_codeentries. - Add the module to a strict override in
pyproject.toml(copy theenable_error_codelist from thetools.kernel.*block). - Run
python scripts/mypy_debt.py --updateand commit the refreshedmypy-baseline.txttogether with the fixes. - Reduce per-file ignores as files become clean; never add new ones to cover new debt.
Refreshing the baseline
Only after paying debt down: python scripts/mypy_debt.py --update,
verify the total dropped, commit. Never hand-edit the baseline upward —
the gate compares totals and per-file counts, so inflating one file
while fixing another still fails.
source: repo docs (build sync)Edit this page on GitHub →