Skip to content

validate-task.yml's lint job pins Python 3.13, narrowing the type check and blocking a uv.lock repo above that floor #1485

Closed

Description

@ptr727

Split out of #1475, which settled the unit-test job's interpreter and said explicitly that "whether the lint job needs the same treatment is a separate call". #1484 took the first half and left this one filed rather than widened into it.

The finding

validate-task.yml's lint job pins its interpreter and exposes no input to change it. Read on the branch #1484 merges:

.github/workflows/validate-task.yml:202    python-version: "3.13"

The python-versions input #1484 adds reaches the unit-test job's matrix alone. Nothing a caller can pass moves the lint job.

Two separate costs, and only one is settleable downstream

A type-check target, which a repository can settle in its own configuration. The Type check Python step runs mypy or pyright under that pinned interpreter. Each defaults its target to the interpreter it runs under, so a repository declaring requires-python = ">=3.13" and testing 3.13 and 3.14 gets its type check performed at 3.13 alone. A 3.14-only typing error, a sys.version_info branch or a typeshed symbol version-gated on one, passes the hub gate with nothing failing and nothing warning. That is the same silent-narrowing shape #1475 was filed for, moved from the test leg to the type checker.

A repository closes this itself by naming a version in its own [tool.mypy] or [tool.pyright] section, and #1484's adopter docs say so. ruff needs nothing, taking its target from requires-python where a [project] table declares one. So this half is a documented workaround rather than a block.

A lockfile floor, which it cannot settle anywhere. The Sync Python dependencies step runs uv sync --all-groups --frozen under the same pinned interpreter, guarded on a committed uv.lock. A repository carrying a uv.lock whose requires-python floor is above the pin fails that step outright: UV_PYTHON resolves to 3.13, which cannot satisfy the lockfile's own requirement. No [tool.*] section reaches it and no value of python-versions reaches it. That repository cannot adopt the gate at all.

No current adopter is in that state. aiopurpleair declares >=3.13 and is unaffected, and a repository whose Python dependencies come from requirements*.txt skips the step entirely, its guard naming uv.lock. It is a constraint on the next adopter rather than a live break, which is why it is filed rather than fixed under #1484.

Suggested remedy, and what makes it not obvious

The shape #1484 used does not transfer unchanged. Its matrix is over interpreters that each run the same tests, where linting the same sources under two interpreters is mostly redundant work: ruff reports identically, and only the type check and the sync actually differ. So a lint-python-version scalar input, defaulting to today's pin, is likely the right size rather than a second matrix, and it would settle both costs at once. A matrix here would double every lint leg, the doc gates included, to re-derive findings the first leg already produced.

Whether the doc-lint block should move out of a job that gains an interpreter dimension is the question to answer first, since it is language-independent and pays for any fan-out it sits inside.

Not filed here

No registry or spec change. No audit report is being written back, and #1484's adopter docs already state both costs so a reader is not misled while this is open.

Activity

  1. ptr727 commented on Nov 1, 2026

    @ptr727
    OwnerAuthor

    Decision needed (decision triage, 2026-11-01)

    Question: How should validate-task.yml's lint job choose its interpreter (setup-uv python-version: "3.13", line 315) so a repository whose uv.lock floor is above 3.13 can adopt the gate: the first python-versions entry, a new lint-python-version scalar input, or a lint matrix with the doc-lint block moved out first?

    Options, recommended first:

    1. Use the first python-versions entry, no new input (recommended): The job already installs a declared pip directory under that entry (validate-task.yml:318-320, bb09334 Gate Each Declared Python Directory on Its Code #1914) and test-matrix legs default to it; the ["3.13"] default keeps today's behavior, settles the lockfile floor, and needs no fan-out, so doc-lint stays put. Needs the same named-cause guard as :345-352. No work in flight.
    2. Add a lint-python-version scalar, default 3.13: The issue's suggestion: explicit, but a second knob on a carried interface that must agree with python-versions and the pip path's rule. No work in flight.
    3. Lint matrix, doc-lint block moved out first: Type-checks at every version but doubles every lint leg, and the doc-lint move is its prerequisite. No work in flight.

    Evidence: validate-task.yml:147-165 (doc-lint in the lint job), :315 (the pin), :337 (uv sync --frozen under it), :345-352 (pip path reads python-versions[0] with a named-cause check). docs/reusable-workflows.md:479 documents the costs. No current adopter is affected.

    Blocks: none

  2. added
    decisionA question waiting on the maintainer, alone or beside whatever else the issue carries
    on Nov 1, 2026
  3. ptr727 commented on Nov 1, 2026

    @ptr727
    OwnerAuthor

    Maintainer answer (decision triage, 2026-11-01): Option 1: the lint job uses the first python-versions entry, with no new input, guarded by the same named-cause check as the pip path. This is now work for the next burndown round.

  4. removed
    decisionA question waiting on the maintainer, alone or beside whatever else the issue carries
    on Nov 1, 2026
  5. ptr727 commented on Nov 1, 2026

    @ptr727
    OwnerAuthor

    Released (2026-11-02): worktree and branch removed, so this claim holds no files. Its fix merged to develop in PR #3796.

    Backlog burndown claim (round 2, group R2G3). Branch: feature/burn-3334. Issues: #3334, #1516, #3487, #1485, #1938.

    Predicted file set:

    • .github/workflows/test-pull-request.yml, .github/workflows/validate-task.yml, .github/workflows/build-release-task.yml
    • catalog/snippets/workflows/test-pull-request.yml, catalog/snippets/workflows/test-pull-request-smoke.yml
    • docs/reusable-workflows.md, WORKFLOW.md, spec/audit.py, spec/files.json, scripts/repo_gate.py
    • spec/project-types.json, spec/fidelity-model.md (added in adjudication)
    • tests/test_audit.py, tests/test_audit_helpers.py, tests/test_release_guards.py, tests/test_repo_gate.py
    • .agents/skills/workflow-ci-contract/ and its generated trees (.github/skills/workflow-ci-contract/, .claude-plugin/fleet-skills/skills/workflow-ci-contract/, .claude-plugin/fleet-skills/.source-digests/workflow-ci-contract)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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