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-quality3description: 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<!-- Generated from harness/github-copilot/plugins/open-horizons-platform/skills/python-code-quality/SKILL.md by harness/claude-code/scripts/convert_from_copilot.py. Edit the source, not this file. -->78# Python Code Quality910## When to invoke1112- Formatting or linting Agent Framework Python changes.13- Running source, test, sample, or Markdown type checks.14- Diagnosing failures from the Python quality or typing CI jobs.1516## Quick Commands1718All commands run from the `python/` directory:1920```bash21# Syntax formatting + checks (parallel across packages by default)22uv run poe syntax23uv run poe syntax -P core24uv run poe syntax -F # Format only25uv run poe syntax -C # Check only26uv run poe syntax -S # Samples only2728# Type checking29#30# Division of labor (see "Type checking architecture" below):31# - Pyright (strict) is the source-code type checker.32# - Pyright (relaxed `basic`), mypy, pyrefly, ty, zuban all check the TESTS;33# pyright/pyrefly/ty also check the SAMPLES (mypy/zuban skip script-style samples).34uv run poe pyright # Pyright (strict) over SOURCE, fan-out across packages35uv run poe pyright -P core36uv run poe pyright -A37uv run poe test-typing # mypy + pyrefly + ty + zuban + pyright over each package's TESTS38uv run poe test-typing -P core39uv run poe test-typing -S # samples (pyrefly + ty + pyright)40uv run poe test-typing -P core --checker mypy # narrow to one checker (repeatable)41uv run poe test-typing -P core --checker pyright # relaxed pyright over the tests42uv run poe mypy # alias: MyPy over the tests only43uv run poe mypy -P core44uv run poe typing # Pyright (source) + the tests checkers45uv run poe typing -P core46uv run poe typing -A4748# All package-level checks in parallel (syntax + pyright)49uv run poe check-packages5051# Full check (packages + samples + tests + markdown)52uv run poe check53uv run poe check -P core5455# Samples only56uv run poe check -S57uv run poe pyright -S5859# Markdown code blocks60uv run poe markdown-code-lint61```6263## Pre-commit Hooks (prek)6465Prek hooks run automatically on commit. They stay lightweight and only check66changed files.6768```bash69# Install hooks70uv run poe prek-install7172# Run all hooks manually73uv run prek run -a7475# Run on last commit76uv run prek run --last-commit77```7879They run changed-package syntax formatting/checking, markdown code lint only80when markdown files change, and sample syntax lint/pyright only when files81under `samples/` change.82They intentionally do not run workspace `pyright` or `mypy` by default.8384## Type checking architecture8586Following the "too many type checkers" approach, type checkers are split by target:8788| Target | Checker(s) | Mode | Config |89|--------|-----------|------|--------|90| Source (`agent_framework*`) | **pyright** | strict | `[tool.pyright]` in `pyproject.toml` |91| Tests | pyright, mypy, pyrefly, ty, zuban | relaxed/basic | `pyrightconfig.tests.json`, `[tool.mypy]`, `pyrefly.toml`, `ty` rules |92| Samples | pyright, pyrefly, ty | basic | `pyrightconfig.samples.json`, `pyrefly.samples.toml`, `ty.samples.toml` |9394- **Pyright is the only *strict* source-code checker**, and it ALSO runs in a relaxed95 `basic` profile over the tests and samples (so the surfaces customers copy from are96 validated by every checker, including pyright). MyPy was removed from source; its97 `[tool.mypy]` block is now a *relaxed* profile used only for tests/samples.98- The extra checkers run over tests/samples because those exercise the public API the way99 users do. The profile is intentionally relaxed (private access allowed, untyped test100 bodies allowed) so authors aren't forced into ugly over-annotation.101- **Gating checkers** are `pyright`, `mypy`, `pyrefly`, `ty`, and `zuban` — all five run by102 default and gate CI. `zuban` is the strictest of the mypy-compatible pair, so the same103 `[tool.mypy]` config yields more findings; suppress zuban-only friction with shared104 `# type: ignore[code]`. Suppress relaxed-pyright friction with `# pyright: ignore[rule]`.105- **Samples** add `pyright` to `pyrefly` + `ty` — mypy/zuban can't resolve script-style106 sample layouts (numeric-prefixed dirs, duplicate `main.py`), but pyright handles them.107- The strict source-pyright (`[tool.pyright]`) enforces `reportUnnecessaryTypeIgnoreComment`108 and excludes tests/samples; the relaxed test/sample pyright configs do not flag unnecessary109 ignores.110111## Ruff Configuration112113- Line length: 120114- Target: Python 3.10+115- Auto-fix enabled116- Rules: ASYNC, B, CPY, D, E, ERA, F, FIX, I, INP, ISC, Q, RET, RSE, RUF, SIM, T20, TD, W, T100, S117- Scripts directory is excluded from checks118119## Pyright Configuration120121- **Source**: strict mode (`[tool.pyright]`), `reportUnnecessaryTypeIgnoreComment = "error"`,122 excludes tests, samples, .venv, packages/devui/frontend.123- **Tests**: relaxed `basic` profile (`pyrightconfig.tests.json`) — private import/usage and124 not-required TypedDict access allowed; runs as the `pyright` checker in `test-typing`.125- **Samples**: relaxed `basic` profile (`pyrightconfig.samples.json`, with a py310 variant) —126 runs as the `pyright` checker in `test-typing -S`.127128## Parallel Execution129130The task runner (`scripts/task_runner.py`) executes the cross-product of131(package × task) in parallel using ThreadPoolExecutor. Single items run132in-process with streaming output.133134## CI Workflow135136CI splits into 4 parallel jobs:1371. **Pre-commit hooks** — lightweight hooks (SKIP=poe-check)1382. **Package checks** — syntax/pyright (source) via check-packages1393. **Samples & markdown** — `check -S` plus `markdown-code-lint`1404. **Test Typing** — change-detected mypy/pyrefly/ty over tests (`ci-test-typing`)141142## Output template143144```markdown145## Python quality result146147- Scope: `<packages, tests, samples, or Markdown>`148- Commands: `<commands run>`149- Result: `<pass or fail>`150- Findings fixed: `<summary>`151- Remaining findings: `<none or details>`152```153154## Quality gate155156- [ ] Checks were scoped to the changed Python surfaces first.157- [ ] Formatting, lint, and relevant type checkers completed successfully.158- [ ] Suppressions are targeted and justified.159- [ ] No generated or unrelated files were reformatted.160- [ ] Remaining failures are reported with their exact command and output.