Skip to content

Maintenance WP2: CI matrix covers Windows/macOS only on Python 3.13; dedupe the copy-pasted job setup #449

Description

@kgdunn

Work package 2 of 3 from the 2026-07-10 maintenance review

This issue is a complete, self-contained work prompt. Anyone (human or agent) should be able to execute it with no other context. Companion packages: WP1 (pytest/tooling hygiene, issue #448) and WP3 (docs API gaps) are separate issues; this one is independent of both.

Background

pyproject.toml declares requires-python = ">=3.10" and classifiers for 3.10 through 3.13, but .github/workflows/run-tests.yml only runs the OS matrix (ubuntu/windows/macos) on Python 3.13; Python 3.10, 3.11, and 3.12 are tested on ubuntu only. The minimum supported Python has never been validated off Linux. Separately, the uv sync --dev --all-extras setup steps are copy-pasted across the lint, typecheck, test, and test-under-dash-O jobs (and docs.yml), so any setup change must be made in five places.

Task A: extend the test matrix

In .github/workflows/run-tests.yml, change the test job matrix from the current shape (OS matrix on 3.13 only, plus ubuntu-only 3.10/3.11/3.12 includes) to:

  • ubuntu-latest: Python 3.10, 3.11, 3.12, 3.13
  • windows-latest: Python 3.10 and 3.13 (minimum and primary)
  • macos-latest: Python 3.10 and 3.13

That is 8 test jobs. Deliberately not the full 12-job product: the two OS extremes on min+max Python catch platform issues without doubling CI cost. Preserve these existing behaviors exactly:

  • Codecov upload happens only on ubuntu + Python 3.13 (keep the existing condition).
  • Keep the existing comment explaining that free-threaded (3.13t/3.14t) builds are excluded (references issue Build & test on free-threaded Python #43).
  • Keep fail-fast setting as-is if present.
  • Windows note: tests/test_tool_safety.py already guards POSIX-only RLIMIT tests with skipif(sys.platform == "win32"), and Windows on 3.13 already passes today, so 3.10 on Windows should be low-risk; if a 3.10-specific failure appears, fix it if it is a test-portability issue (path separators, line endings), otherwise report it on this issue rather than papering over it.

Task B: composite action for environment setup

Create .github/actions/setup-env/action.yml, a composite action that encapsulates the repeated setup sequence used by the jobs in run-tests.yml and docs.yml. Look at the current jobs first and mirror them exactly; as of the audit the repeated sequence is: checkout is done by the job itself (leave actions/checkout in each job), then install uv (the repo uses astral-sh/setup-uv; keep the same action and version), then uv python install <version> or equivalent pinning, then uv sync --dev --all-extras.

The composite action should take one input, python-version (string, required), and perform: setup-uv, then uv sync --dev --all-extras with the requested interpreter. Replace the inline steps in the lint, typecheck, test, and test-under-dash-O jobs of run-tests.yml and the build job of docs.yml with uses: ./.github/actions/setup-env. This must be a behavior-preserving refactor: same uv version pin, same cache configuration (if setup-uv caching is enabled today, keep it), same flags.

Version bump and metadata (last commit of the PR)

Constraints

  • Branch: claude/process-improve-maintenance-review-0lbiru-wp2 (or restart from latest main if it exists with merged history); one ready-for-review PR for this package, micro-committed (matrix change and composite action as separate commits).
  • Never stage or push lock files.
  • No em-dash characters in committed prose, commit messages, or the PR description (repo convention).

Verification

  1. Workflow YAML validates (run actionlint if available, otherwise careful review plus a push to the PR branch).
  2. All 8 matrix legs plus lint, typecheck, test-under-dash-O, and docs jobs green on the PR.
  3. Confirm in the CI logs that the composite action ran in every job and that codecov still uploaded exactly once (ubuntu, 3.13).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions