Python Code Quality
When to invoke
- Formatting or linting Agent Framework Python changes.
- Running source, test, sample, or Markdown type checks.
- Diagnosing failures from the Python quality or typing CI jobs.
Quick Commands
All commands run from the python/ directory:
# Syntax formatting + checks (parallel across packages by default)
uv run poe syntax
uv run poe syntax -P core
uv run poe syntax -F # Format only
uv run poe syntax -C # Check only
uv run poe syntax -S # Samples only
# Type checking
#
# Division of labor (see "Type checking architecture" below):
# - Pyright (strict) is the source-code type checker.
# - Pyright (relaxed `basic`), mypy, pyrefly, ty, zuban all check the TESTS;
# pyright/pyrefly/ty also check the SAMPLES (mypy/zuban skip script-style samples).
uv run poe pyright # Pyright (strict) over SOURCE, fan-out across packages
uv run poe pyright -P core
uv run poe pyright -A
uv run poe test-typing # mypy + pyrefly + ty + zuban + pyright over each package's TESTS
uv run poe test-typing -P core
uv run poe test-typing -S # samples (pyrefly + ty + pyright)
uv run poe test-typing -P core --checker mypy # narrow to one checker (repeatable)
uv run poe test-typing -P core --checker pyright # relaxed pyright over the tests
uv run poe mypy # alias: MyPy over the tests only
uv run poe mypy -P core
uv run poe typing # Pyright (source) + the tests checkers
uv run poe typing -P core
uv run poe typing -A
# All package-level checks in parallel (syntax + pyright)
uv run poe check-packages
# Full check (packages + samples + tests + markdown)
uv run poe check
uv run poe check -P core
# Samples only
uv run poe check -S
uv run poe pyright -S
# Markdown code blocks
uv run poe markdown-code-lint
Pre-commit Hooks (prek)
Prek hooks run automatically on commit. They stay lightweight and only check
changed files.
# Install hooks
uv run poe prek-install
# Run all hooks manually
uv run prek run -a
# Run on last commit
uv run prek run --last-commit
They run changed-package syntax formatting/checking, markdown code lint only
when markdown files change, and sample syntax lint/pyright only when files
under samples/ change.
They intentionally do not run workspace pyright or mypy by default.
Type checking architecture
Following the "too many type checkers" approach, type checkers are split by target:
| Target |
Checker(s) |
Mode |
Config |
Source (agent_framework*) |
pyright |
strict |
[tool.pyright] in pyproject.toml |
| Tests |
pyright, mypy, pyrefly, ty, zuban |
relaxed/basic |
pyrightconfig.tests.json, [tool.mypy], pyrefly.toml, ty rules |
| Samples |
pyright, pyrefly, ty |
basic |
pyrightconfig.samples.json, pyrefly.samples.toml, ty.samples.toml |
- Pyright is the only strict source-code checker, and it ALSO runs in a relaxed
basic profile over the tests and samples (so the surfaces customers copy from are
validated by every checker, including pyright). MyPy was removed from source; its
[tool.mypy] block is now a relaxed profile used only for tests/samples.
- The extra checkers run over tests/samples because those exercise the public API the way
users do. The profile is intentionally relaxed (private access allowed, untyped test
bodies allowed) so authors aren't forced into ugly over-annotation.
- Gating checkers are
pyright, mypy, pyrefly, ty, and zuban — all five run by
default and gate CI. zuban is the strictest of the mypy-compatible pair, so the same
[tool.mypy] config yields more findings; suppress zuban-only friction with shared
# type: ignore[code]. Suppress relaxed-pyright friction with # pyright: ignore[rule].
- Samples add
pyright to pyrefly + ty — mypy/zuban can't resolve script-style
sample layouts (numeric-prefixed dirs, duplicate main.py), but pyright handles them.
- The strict source-pyright (
[tool.pyright]) enforces reportUnnecessaryTypeIgnoreComment
and excludes tests/samples; the relaxed test/sample pyright configs do not flag unnecessary
ignores.
Ruff Configuration
- Line length: 120
- Target: Python 3.10+
- Auto-fix enabled
- Rules: ASYNC, B, CPY, D, E, ERA, F, FIX, I, INP, ISC, Q, RET, RSE, RUF, SIM, T20, TD, W, T100, S
- Scripts directory is excluded from checks
Pyright Configuration
- Source: strict mode (
[tool.pyright]), reportUnnecessaryTypeIgnoreComment = "error",
excludes tests, samples, .venv, packages/devui/frontend.
- Tests: relaxed
basic profile (pyrightconfig.tests.json) — private import/usage and
not-required TypedDict access allowed; runs as the pyright checker in test-typing.
- Samples: relaxed
basic profile (pyrightconfig.samples.json, with a py310 variant) —
runs as the pyright checker in test-typing -S.
Parallel Execution
The task runner (scripts/task_runner.py) executes the cross-product of
(package × task) in parallel using ThreadPoolExecutor. Single items run
in-process with streaming output.
CI Workflow
CI splits into 4 parallel jobs:
- Pre-commit hooks — lightweight hooks (SKIP=poe-check)
- Package checks — syntax/pyright (source) via check-packages
- Samples & markdown —
check -S plus markdown-code-lint
- Test Typing — change-detected mypy/pyrefly/ty over tests (
ci-test-typing)
Output template
## Python quality result
- Scope: `<packages, tests, samples, or Markdown>`
- Commands: `<commands run>`
- Result: `<pass or fail>`
- Findings fixed: `<summary>`
- Remaining findings: `<none or details>`
Quality gate
1---2name: python-code-quality-23description: Code quality checks, linting, formatting, and type checking commands for the Agent Framework Python codebase. Use when running checks, fixing lint errors, or troubleshooting CI failures.4---56# Python Code Quality78## When to invoke910- Formatting or linting Agent Framework Python changes.11- Running source, test, sample, or Markdown type checks.12- Diagnosing failures from the Python quality or typing CI jobs.1314## Quick Commands1516All commands run from the `python/` directory:1718```bash19# Syntax formatting + checks (parallel across packages by default)20uv run poe syntax21uv run poe syntax -P core22uv run poe syntax -F # Format only23uv run poe syntax -C # Check only24uv run poe syntax -S # Samples only2526# Type checking27#28# Division of labor (see "Type checking architecture" below):29# - Pyright (strict) is the source-code type checker.30# - Pyright (relaxed `basic`), mypy, pyrefly, ty, zuban all check the TESTS;31# pyright/pyrefly/ty also check the SAMPLES (mypy/zuban skip script-style samples).32uv run poe pyright # Pyright (strict) over SOURCE, fan-out across packages33uv run poe pyright -P core34uv run poe pyright -A35uv run poe test-typing # mypy + pyrefly + ty + zuban + pyright over each package's TESTS36uv run poe test-typing -P core37uv run poe test-typing -S # samples (pyrefly + ty + pyright)38uv run poe test-typing -P core --checker mypy # narrow to one checker (repeatable)39uv run poe test-typing -P core --checker pyright # relaxed pyright over the tests40uv run poe mypy # alias: MyPy over the tests only41uv run poe mypy -P core42uv run poe typing # Pyright (source) + the tests checkers43uv run poe typing -P core44uv run poe typing -A4546# All package-level checks in parallel (syntax + pyright)47uv run poe check-packages4849# Full check (packages + samples + tests + markdown)50uv run poe check51uv run poe check -P core5253# Samples only54uv run poe check -S55uv run poe pyright -S5657# Markdown code blocks58uv run poe markdown-code-lint59```6061## Pre-commit Hooks (prek)6263Prek hooks run automatically on commit. They stay lightweight and only check64changed files.6566```bash67# Install hooks68uv run poe prek-install6970# Run all hooks manually71uv run prek run -a7273# Run on last commit74uv run prek run --last-commit75```7677They run changed-package syntax formatting/checking, markdown code lint only78when markdown files change, and sample syntax lint/pyright only when files79under `samples/` change.80They intentionally do not run workspace `pyright` or `mypy` by default.8182## Type checking architecture8384Following the "too many type checkers" approach, type checkers are split by target:8586| Target | Checker(s) | Mode | Config |87|--------|-----------|------|--------|88| Source (`agent_framework*`) | **pyright** | strict | `[tool.pyright]` in `pyproject.toml` |89| Tests | pyright, mypy, pyrefly, ty, zuban | relaxed/basic | `pyrightconfig.tests.json`, `[tool.mypy]`, `pyrefly.toml`, `ty` rules |90| Samples | pyright, pyrefly, ty | basic | `pyrightconfig.samples.json`, `pyrefly.samples.toml`, `ty.samples.toml` |9192- **Pyright is the only *strict* source-code checker**, and it ALSO runs in a relaxed93 `basic` profile over the tests and samples (so the surfaces customers copy from are94 validated by every checker, including pyright). MyPy was removed from source; its95 `[tool.mypy]` block is now a *relaxed* profile used only for tests/samples.96- The extra checkers run over tests/samples because those exercise the public API the way97 users do. The profile is intentionally relaxed (private access allowed, untyped test98 bodies allowed) so authors aren't forced into ugly over-annotation.99- **Gating checkers** are `pyright`, `mypy`, `pyrefly`, `ty`, and `zuban` — all five run by100 default and gate CI. `zuban` is the strictest of the mypy-compatible pair, so the same101 `[tool.mypy]` config yields more findings; suppress zuban-only friction with shared102 `# type: ignore[code]`. Suppress relaxed-pyright friction with `# pyright: ignore[rule]`.103- **Samples** add `pyright` to `pyrefly` + `ty` — mypy/zuban can't resolve script-style104 sample layouts (numeric-prefixed dirs, duplicate `main.py`), but pyright handles them.105- The strict source-pyright (`[tool.pyright]`) enforces `reportUnnecessaryTypeIgnoreComment`106 and excludes tests/samples; the relaxed test/sample pyright configs do not flag unnecessary107 ignores.108109## Ruff Configuration110111- Line length: 120112- Target: Python 3.10+113- Auto-fix enabled114- Rules: ASYNC, B, CPY, D, E, ERA, F, FIX, I, INP, ISC, Q, RET, RSE, RUF, SIM, T20, TD, W, T100, S115- Scripts directory is excluded from checks116117## Pyright Configuration118119- **Source**: strict mode (`[tool.pyright]`), `reportUnnecessaryTypeIgnoreComment = "error"`,120 excludes tests, samples, .venv, packages/devui/frontend.121- **Tests**: relaxed `basic` profile (`pyrightconfig.tests.json`) — private import/usage and122 not-required TypedDict access allowed; runs as the `pyright` checker in `test-typing`.123- **Samples**: relaxed `basic` profile (`pyrightconfig.samples.json`, with a py310 variant) —124 runs as the `pyright` checker in `test-typing -S`.125126## Parallel Execution127128The task runner (`scripts/task_runner.py`) executes the cross-product of129(package × task) in parallel using ThreadPoolExecutor. Single items run130in-process with streaming output.131132## CI Workflow133134CI splits into 4 parallel jobs:1351. **Pre-commit hooks** — lightweight hooks (SKIP=poe-check)1362. **Package checks** — syntax/pyright (source) via check-packages1373. **Samples & markdown** — `check -S` plus `markdown-code-lint`1384. **Test Typing** — change-detected mypy/pyrefly/ty over tests (`ci-test-typing`)139140## Output template141142```markdown143## Python quality result144145- Scope: `<packages, tests, samples, or Markdown>`146- Commands: `<commands run>`147- Result: `<pass or fail>`148- Findings fixed: `<summary>`149- Remaining findings: `<none or details>`150```151152## Quality gate153154- [ ] Checks were scoped to the changed Python surfaces first.155- [ ] Formatting, lint, and relevant type checkers completed successfully.156- [ ] Suppressions are targeted and justified.157- [ ] No generated or unrelated files were reformatted.158- [ ] Remaining failures are reported with their exact command and output.