Skip to content

ci(python-sdk): enforce mypy with module-by-module allowlist ratchet (#971) - #1052

Merged
AbirAbbas merged 4 commits into
Agent-Field:mainfrom
7vignesh:feat/971-python-sdk-mypy-ci
Sep 18, 2026
Merged

AbirAbbas merged 4 commits into
Agent-Field:mainfrom
7vignesh:feat/971-python-sdk-mypy-ci

Conversation

@7vignesh

Copy link
Copy Markdown
Contributor

Summary

Adopts the mypy half of #620 as phase 1 of #971. The ruff half shipped in #812, but the mypy half was never wired up: sdk/python/mypy.ini existed and nothing ran it. This adds a required mypy step to the Python SDK CI with a module-by-module allowlist, so the gate can be turned on immediately (zero behavior change) and the allowlist shrunk module by module.

Closes #971 (phase 1). Refs #620.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Docs only
  • Tests only
  • CI / tooling
  • Breaking change

What changed

  • Pin mypy==1.18.2 in the dev extras (pyproject.toml) and regenerate uv.lock.
  • Add a per-module ignore_errors = True allowlist to mypy.ini for the 34 modules that currently have type errors (217 errors total), with a header comment describing the ratchet and how to shrink it.
  • Add a Type check (mypy) step to .github/workflows/sdk-python.yml running mypy --config-file mypy.ini agentfield/.

No # type: ignore sprinkles were added, and no agentfield/ source was modified.

Validation contract (from #971)

  • CI fails if a non-allowlisted module gains a new mypy error. Verified: injecting a type error into a clean module (agent_cli.py) makes mypy exit 1.
  • Removing a module from the allowlist while it still has errors fails CI. Verified: removing agentfield.types re-surfaces its errors (exit 1).
  • pip install .[dev] provides the pinned mypy version used in CI.

Test plan

  • cd sdk/python && mypy --config-file mypy.ini agentfield/ -> Success: no issues found in 79 source files (exit 0)
  • cd sdk/python && ruff check . -> All checks passed!
  • cd sdk/python && uv lock --check -> clean after regenerating uv.lock
  • python -c "import agentfield" -> imports OK
  • Ratchet invariants verified (see Validation contract above)

Test coverage

  • I ran tests for the surface(s) I changed locally.
  • No production code changed; this is CI/tooling only, so coverage is unaffected.
  • The coverage gate check is green in CI before requesting review.

Checklist

  • I have read CONTRIBUTING.md (if present) and docs/DEVELOPMENT.md.
  • Commits are signed and follow conventional-commits style. (conventional-commits: yes; commit signing not configured in my environment)
  • I have linked any related issues.

Related issues / PRs

Closes #971 (phase 1). Refs #620, #812.

Notes for reviewers

  • The mypy step runs in the existing lint-and-test matrix (Python 3.10-3.13). mypy.ini pins python_version = 3.10, so results are identical across matrix rows; running it in every row is redundant but harmless. Happy to move it to a single dedicated job if you prefer.
  • Phase 2 (cleaning the async-heavy modules first, per the issue's suggested order) is intentionally out of scope for this PR to keep it a pure, reviewable wiring change.

Adopts the mypy half of Agent-Field#620 (the ruff half shipped in Agent-Field#812). mypy.ini
existed but nothing ran it. This wires a required mypy step into the
Python SDK CI without changing any behavior.

Phase 1 of Agent-Field#971:
- Pin mypy==1.18.2 in the dev extras (and uv.lock).
- Add a per-module ignore_errors allowlist to mypy.ini for the 34
  modules that currently have type errors, so the gate can be enforced
  now and the allowlist shrunk module by module.
- Add a 'Type check (mypy)' step to .github/workflows/sdk-python.yml
  running 'mypy --config-file mypy.ini agentfield/'.

The gate is green today (Success: no issues found). It fails if a
non-allowlisted module gains a new error, or if a module is removed
from the allowlist while it still has errors. No '# type: ignore'
sprinkles were added.
@7vignesh
7vignesh requested review from a team and AbirAbbas as code owners September 10, 2026 22:37
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Performance

SDK Memory Δ Latency Δ Tests Status
Python 9.0 KB - 0.31 µs -11%

✓ No regressions detected

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage gate

Thresholds from .coverage-gate.toml: per-surface ≥ 84%, aggregate ≥ 85%, max per-surface regression ≤ 1.0 pp, max aggregate regression ≤ 0.50 pp.

Surface Current Baseline Δ
control-plane 87.90% 87.40% ↑ +0.50 pp 🟡
sdk-go 93.20% 92.00% ↑ +1.20 pp 🟢
sdk-python 94.72% 93.73% ↑ +0.99 pp 🟢
sdk-typescript 91.76% 90.42% ↑ +1.34 pp 🟢
web-ui 84.77% 84.79% ↓ -0.02 pp 🟡
aggregate 85.92% 85.75% ↑ +0.17 pp 🟡

✅ Gate passed

No surface regressed past the allowed threshold and the aggregate stayed above the floor.

@github-actions

Copy link
Copy Markdown
Contributor

📐 Patch coverage gate

Threshold: 80% on lines this PR touches vs origin/main (from .coverage-gate.toml:thresholds.min_patch).

Surface Touched lines Patch coverage Status
control-plane 0 ➖ no changes
sdk-go 0 ➖ no changes
sdk-python 0 ➖ no changes
sdk-typescript 0 ➖ no changes
web-ui 0 ➖ no changes

✅ Patch gate passed

Every surface whose lines were touched by this PR has patch coverage at or above the threshold.

@santoshkumarradha santoshkumarradha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ratchet structure makes sense, but the checked-in environment cannot currently run the new gate.

Comment thread sdk/python/pyproject.toml
Addresses review on Agent-Field#1052. The locked dev environment pins
requests 2.33.1, which ships no type stubs, so 'uv sync --extra dev'
followed by mypy failed with [import-untyped] in multimodal.py and
agent_field_handler.py. CI was green only because 'pip install .[dev]'
resolved a newer requests that bundles stubs, so it wasn't validating
the reproducible environment.

Pin types-requests==2.33.0.20260906 in the dev extras (and uv.lock) so
mypy passes in both the locked (uv sync) and pip-resolved environments.
Verified mypy clean against the locked requests 2.33.1 and a newer
requests; ruff still clean; uv lock --check clean.
@7vignesh

Copy link
Copy Markdown
Contributor Author

Fixed in e57eaf7. You're right - the gate wasn't reproducible.

Reproduced exactly: after uv sync --python 3.13 --extra dev, the locked requests 2.33.1 ships no stubs, so mypy failed with [import-untyped] in multimodal.py and agent_field_handler.py. CI was green only because pip install .[dev] pulled a newer requests that bundles its own stubs.

Went with your first suggestion: pinned types-requests==2.33.0.20260906 in the dev extras and regenerated uv.lock. I chose the stub package over allowlisting those two modules because it keeps them type-clean (allowlisting would suppress all their errors, not just the requests import), and over switching CI to uv sync because the stub pin fixes both paths without restructuring the workflow.

Verified mypy Success: no issues found against both the locked requests 2.33.1 and a newer pip-resolved requests; ruff check clean; uv lock --check clean.

7vignesh and others added 2 commits September 15, 2026 19:56
session_turn_detection.py landed on main (via Agent-Field#1056) after this PR
opened and carries 3 mypy errors (loosely-typed dict value used in
numeric comparisons at line 58). The mypy gate correctly failed on it
because it was not allowlisted - the ratchet working as designed.

Add it to the allowlist to keep this PR a pure CI-wiring change; the
module's type errors can be cleaned in a follow-up (phase 2).

@santoshkumarradha santoshkumarradha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing the reproducible typing environment. I rechecked the updated head with Python 3.13: the locked dev sync, full mypy gate, Ruff, and diff check all pass. The new allowlist entry matches the module added on current main, so this is ready from review.

@AbirAbbas AbirAbbas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran the workflow's exact steps on the merge with main in three environments: fresh pip 3.10, fresh pip 3.13, and the locked uv sync --python 3.13 --extra dev env Santosh used. ruff 0.15.22, mypy --config-file mypy.ini agentfield/ (80 files, clean) and run_pytest.sh pass in all of them, uv lock --check is clean, and the types-requests pin installs on 3.10/3.11/3.12/3.13. The gate is real: a type error injected into an unlisted module fails CI, one in a listed dirty module is suppressed, which is the ratchet contract. All 35 listed modules exist on main. Good to merge.

@AbirAbbas
AbirAbbas merged commit 8c3e084 into Agent-Field:main Sep 18, 2026
41 of 43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Python SDK] Adopt mypy in CI (module-by-module ratchet)

3 participants