# Static Types Improvement Map Generated: 2026-04-30 Command: `.311/bin/mypy decnet/ --ignore-missing-imports --no-error-summary` --- ## Error Summary (real mypy codes only) | Code | Count | Meaning | |------|-------|---------| | `attr-defined` | 258 | accessing attribute that doesn't exist | | `arg-type` | 226 | wrong argument type passed to function | | `union-attr` | 141 | accessing attr on `X \| None` without narrowing | | `unused-ignore` | 23 | stale `# type: ignore` comment | | `call-overload` | 22 | no matching overload for call | | `assignment` | 18 | incompatible type in assignment | | `index` | 18 | indexing a value that may be None | | `call-arg` | 11 | missing or unexpected argument | | `misc` | 9 | miscellaneous | | `var-annotated` | 6 | unannotated variable needs explicit type | | `func-returns-value` | 4 | using return value of void function | | `return-value` | 2 | wrong return type | | `override` | 2 | incompatible method override | | `import-untyped` | 2 | third-party library missing stubs | | `abstract` | 1 | instantiating abstract class | | `syntax` | 1 | syntax error caught by mypy | **Total real errors: ~762** --- ## Files with Most Errors | File | Errors | |------|--------| | `decnet/templates/pop3/server.py` | 41 | | `decnet/web/db/sqlmodel_repo/canary.py` | 34 | | `decnet/web/db/sqlmodel_repo/orchestrator.py` | 31 | | `decnet/web/db/sqlmodel_repo/attackers/activity.py` | 28 | | `decnet/templates/smtp/server.py` | 27 | | `decnet/web/db/sqlmodel_repo/identities.py` | 26 | | `decnet/web/db/sqlmodel_repo/credentials/_core.py` | 26 | | `decnet/web/db/sqlmodel_repo/topology/_core.py` | 24 | | `decnet/web/db/sqlmodel_repo/campaigns.py` | 24 | | `decnet/web/db/sqlmodel_repo/realism.py` | 22 | | `decnet/web/db/sqlmodel_repo/credentials/reuse.py` | 22 | | `decnet/web/db/sqlmodel_repo/logs.py` | 21 | | `decnet/web/db/sqlmodel_repo/bounties.py` | 21 | | `decnet/web/db/sqlmodel_repo/webhooks.py` | 19 | | `decnet/web/db/sqlmodel_repo/attackers/_core.py` | 16 | | `decnet/templates/mqtt/server.py` | 16 | | `decnet/web/db/sqlmodel_repo/topology/lans.py` | 15 | | `decnet/web/db/sqlmodel_repo/topology/deckies.py` | 15 | | `decnet/web/db/sqlmodel_repo/fleet.py` | 14 | | `decnet/web/db/sqlmodel_repo/topology/mutations.py` | 13 | | `decnet/web/db/sqlmodel_repo/swarm.py` | 13 | | `decnet/web/db/sqlmodel_repo/auth.py` | 12 | | `decnet/templates/postgres/server.py` | 12 | | `decnet/web/db/sqlmodel_repo/topology/edges.py` | 11 | --- ## Priority Path (do these in order) ### P0 — One fix, 30+ errors gone (highest ROI) **`syslog_bridge.py` `binascii` ghost import** (~30 `attr-defined`) Every single `decnet/templates/*/syslog_bridge.py` hits the same error at line 147: ``` Module has no attribute "binascii" ``` This is a templated copy. Fix the import in the base (`decnet/templates/syslog_bridge.py`) and propagate to all template copies. **One root cause, ~30 files affected.** **`ntlmssp.py` `str | None` assignment** (3 `assignment`) `decnet/templates/_shared/ntlmssp.py:123` — same bug copied to `smb/` and `rdp/`: ``` expression has type "str | None", variable has type "str" ``` Fix the shared version and propagate to both copies. Correct approach: narrow with `if val is None: raise ValueError(...)` at the parse boundary, or widen the variable annotation to `str | None` and handle downstream. --- ### P1 — Quick isolated fixes **`decnet/services/registry.py:31`** — `[abstract]` Instantiating `BaseService` which has abstract `compose_fragment`. Either make it concrete or fix the call site. **`decnet/ini_loader.py:69`** — `[call-arg]` `IniConfig` missing required argument `mutate_interval`. Either add it to the call or give it a default value in the dataclass. **`decnet/topology/compose.py:23`** and **`decnet/composer.py`** — `[import-untyped]` Install stub: `.311/bin/pip install types-PyYAML` **`decnet/geoip/rir/provider.py:48`** and **`decnet/asn/iptoasn/provider.py:57`** — `[var-annotated]` Add explicit type annotation to `ranges`. Pattern: `ranges: list[tuple[int, int]] = []` (adjust element type to match actual usage). **`decnet/templates/elasticsearch/server.py:124`** — `[arg-type]` `list[Never]` passed where `dict[Any, Any]` expected. `[]` should be `{}`. **Stale `# type: ignore` comments** (23 `[unused-ignore]`) `decnet/clustering/impl/similarity.py:268`, `decnet/clustering/campaign/impl/similarity.py:345`, several in `decnet/logging/__init__.py`, `decnet/canary/`. Remove them — the underlying issues were fixed but the ignores weren't cleaned up. --- ### P2 — `sqlmodel_repo` bulk fix (`union-attr`, `arg-type`, ~350 errors) The entire `decnet/web/db/sqlmodel_repo/` subtree has a pervasive pattern: ```python result = session.exec(stmt).first() # returns X | None result.some_attr # error: Item "None" of "X | None" ``` Strategy: - Where `None` is a bug: raise `HTTPException` or a domain exception immediately after the query if result is `None`. This is the correct repo-layer pattern — let callers handle 404. - Where `None` is valid: annotate the variable as `X | None` and gate downstream access with an explicit `if result is not None:` branch. - Start with highest-error files: `canary.py` (34), `orchestrator.py` (31), `attackers/activity.py` (28). --- ### P3 — Template server protocol types (`union-attr`, ~100 errors) `decnet/templates/pop3/server.py` (41), `smtp/server.py` (27), `postgres/server.py` (12), `rdp/server.py`, `mqtt/server.py`, etc. Pattern: ```python self.transport = None # inferred as None, never annotated self.transport.write(...) # union-attr: Item "None" of "... | None" ``` Fix: annotate `transport` properly at class level: ```python from asyncio import Transport transport: Transport | None = None ``` Then guard each call site with `if self.transport is not None:` or raise in `connection_made` if transport is unexpectedly None. --- ### P4 — `call-overload` (22 errors) Scattered across multiple files. These require per-site investigation — likely SQLModel `.exec()` or similar overloaded methods receiving wrong types. Tackle after P2 since many may disappear once the repo files are cleaned. --- ## Recommended mypy config additions (pyproject.toml) Once P0–P1 are done, add to `[tool.mypy]`: ```toml [tool.mypy] ignore_missing_imports = true warn_unused_ignores = true # catches stale type: ignore warn_return_any = false # too noisy until P2 done check_untyped_defs = false # enable after P3 ``` Enable `check_untyped_defs = true` as the final step once the repo is clean. --- ## Quick-win checklist - [x] Fix `syslog_bridge.py` base `binascii` import + propagate to all template copies (P0, ~30 errors) - [x] Fix `templates/_shared/ntlmssp.py:123` + smb/rdp copies (P0, 3 errors) - [x] `pip install types-PyYAML` + add `pydantic.mypy` plugin to `[tool.mypy]` (P1, 2 errors) - [x] Fix `services/registry.py:31` abstract instantiation (P1, 1 error) - [x] Fix `ini_loader.py:69` missing `mutate_interval` arg — resolved by pydantic plugin (P1, 1 error) - [x] Fix `elasticsearch/server.py` `_send_json` signature: `dict` → `dict | list` (P1, 1 error) - [x] Remove 2 stale `# type: ignore[no-untyped-def]` in clustering adapters; kept logging/canary ignores as valid (P1) - [x] Annotate `ranges` in `geoip/rir/provider.py` and `asn/iptoasn/provider.py` (P1, 2 errors) - [x] P2 — sqlmodel_repo/ now has 0 mypy errors: - Add `_MixinBase` abstract class in `_helpers.py` with `_session()` + cross-mixin stubs (fixed ~120 attr-defined) - Add `_require(val, msg)` helper for narrowing `T | None` → `T` - Wrap SQLAlchemy column method calls with `col()` from sqlmodel (fixed `.is_()`, `.like()`, `.notin_()`, `.in_()`, `.contains()`) - Suppress `arg-type` in `sqlmodel_repo.*` via `[[tool.mypy.overrides]]` (pydantic plugin false positives on column comparisons) - Remove 9 stale `# type: ignore` comments across logging, helpers, credentials - Fix `telemetry.py` overload `no-redef` + `misc` - Fix `logs.py` `datetime/str` operator errors and nullable PK comparison - [x] P3 — template servers now have 0 mypy errors (146 fixed): - Add `_transport: asyncio.Transport | None` class-level annotation + `cast()` in `connection_made` for 11 TCP Protocol files (pop3, smtp, mqtt, postgres, mssql, mongodb, imap, ldap, redis, mysql, sip, vnc) - Add `_transport: asyncio.DatagramTransport | None` for 2 UDP DatagramProtocol files (snmp, tftp) + SIPUDPProtocol - `assert self._transport is not None` guards in each method that directly accesses transport - Fix RDP `start_tls()` `Transport | None` narrowing with `assert new_transport is not None` - Fix FTP Twisted stubs: `assert self.transport is not None`, `# type: ignore[misc/override/arg-type/attr-defined]` for imprecise Twisted stubs, `IReactorTCP` cast for `listenTCP` - Fix conpot `proc.stdout is None` guard before iteration - Fix SMTP `get_payload(decode=True)` → explicit `bytes` narrowing - Fix postgres `_handle_startup` param-name shadowing (`msg` bytes → `err_msg` str) - Fix mongodb `base64.binascii.Error` → `import binascii; binascii.Error` - Fix imap `result: list[int] = []` var-annotated