Severity
LOW
Area
packaging / ci
Problem
The four reusable Python workflows (python-lint.yml, python-typecheck.yml, python-ci.yml, python-tests.yml) use pip install -e "<install-spec>" for dependency setup. The project itself is managed with uv and ships a uv.lock, so CI does not install the locked dependency set — it resolves fresh from PyPI on every run against >= constraints in pyproject.toml.
This means:
- CI can drift from local developer environments and from each previous CI run.
uv.lock is authoritative locally but ignored by CI.
- Cold-cache CI installs are slower than
uv sync would be.
- Each workflow re-implements pip cache + install logic instead of using a single
astral-sh/setup-uv action.
This was raised as the broader scope option during the dev-tooling cleanup (#380); that PR took the minimal path (drop ad-hoc pip install ruff/mypy, declare them in pyproject.toml) and explicitly deferred the full migration to here.
Evidence
.github/workflows/python-lint.yml — python -m pip install -e "${{ inputs.install-spec }}"
.github/workflows/python-typecheck.yml — same
.github/workflows/python-ci.yml — same (build job)
.github/workflows/python-tests.yml — same (matrix test job)
uv.lock exists at repo root and is the source of truth for local uv sync.
Impact
- Reproducibility: CI green != local green is a real risk when an upstream releases a breaking patch.
- Speed:
uv sync with a warm cache is significantly faster than pip install; cold installs are also faster.
- Maintainability: Four workflows duplicate the same setup pattern.
- Consistency:
AGENTS.md tells contributors to use uv sync / uv run; CI should follow.
Proposed Direction
Migrate the four reusable workflows to use uv end-to-end:
- Replace
actions/setup-python + pip install with astral-sh/setup-uv@v6 + uv sync --extra <group>.
- Replace
python -m <tool> invocations with uv run <tool>.
- Keep the
install-spec input but interpret it as the extras list passed to uv sync (e.g. dev, dev,test). Alternatively introduce a new extras input and deprecate install-spec.
- Update
ci.yml callers to pass the new shape.
Out of scope:
- Removing the reusable-workflow pattern (they should remain consumable from other repos).
- Changing the test matrix (
3.11, 3.12, 3.13).
- Touching
python-release.yml or python-gitleaks.yml (not pip-based in the same way).
Acceptance Criteria
Dependencies
Suggested Labels
severity:low
area:packaging
ci
type:refactor
enhancement
python:uv
Notes
This is intentionally scoped as a single coordinated change rather than per-workflow PRs, because the install-spec input shape change ripples through ci.yml. Doing it once keeps the diff coherent.
Estimated Refactor Risk
Low — CI-only change, no runtime code touched. Risk is "CI is broken until fixed" not "library is broken".
Epic
I: Packaging & Docs
Severity
LOW
Area
packaging / ci
Problem
The four reusable Python workflows (
python-lint.yml,python-typecheck.yml,python-ci.yml,python-tests.yml) usepip install -e "<install-spec>"for dependency setup. The project itself is managed withuvand ships auv.lock, so CI does not install the locked dependency set — it resolves fresh from PyPI on every run against>=constraints inpyproject.toml.This means:
uv.lockis authoritative locally but ignored by CI.uv syncwould be.astral-sh/setup-uvaction.This was raised as the broader scope option during the dev-tooling cleanup (#380); that PR took the minimal path (drop ad-hoc
pip install ruff/mypy, declare them inpyproject.toml) and explicitly deferred the full migration to here.Evidence
.github/workflows/python-lint.yml—python -m pip install -e "${{ inputs.install-spec }}".github/workflows/python-typecheck.yml— same.github/workflows/python-ci.yml— same (build job).github/workflows/python-tests.yml— same (matrix test job)uv.lockexists at repo root and is the source of truth for localuv sync.Impact
uv syncwith a warm cache is significantly faster thanpip install; cold installs are also faster.AGENTS.mdtells contributors to useuv sync/uv run; CI should follow.Proposed Direction
Migrate the four reusable workflows to use
uvend-to-end:actions/setup-python+pip installwithastral-sh/setup-uv@v6+uv sync --extra <group>.python -m <tool>invocations withuv run <tool>.install-specinput but interpret it as the extras list passed touv sync(e.g.dev,dev,test). Alternatively introduce a newextrasinput and deprecateinstall-spec.ci.ymlcallers to pass the new shape.Out of scope:
3.11,3.12,3.13).python-release.ymlorpython-gitleaks.yml(not pip-based in the same way).Acceptance Criteria
python-lint.ymlusesastral-sh/setup-uv+uv sync+uv run ruff check ...python-typecheck.ymlusesastral-sh/setup-uv+uv sync+uv run mypy ...python-ci.yml(build) usesuv build(oruv run python -m build)python-tests.ymlmatrix usesastral-sh/setup-uv+uv sync+uv run pytest ...ci.ymlcallers updated; existingvars.CI_PYTHON_*overrides preserved or cleanly renameduv.lockis the dependency source on CI (locked installs, no resolution drift)AGENTS.md"Commands" section reviewed to confirm it matches what CI now runsDependencies
auditextra) and deps: declare ruff + mypy in dev extra, pin via uv.lock #380 (devextra) — both add extras thatuv synccalls will reference.Suggested Labels
severity:lowarea:packagingcitype:refactorenhancementpython:uvNotes
This is intentionally scoped as a single coordinated change rather than per-workflow PRs, because the
install-specinput shape change ripples throughci.yml. Doing it once keeps the diff coherent.Estimated Refactor Risk
Low — CI-only change, no runtime code touched. Risk is "CI is broken until fixed" not "library is broken".
Epic
I: Packaging & Docs