From 8ebdb7d24f60e4c7f9877969178d86912f9552b4 Mon Sep 17 00:00:00 2001 From: Alessandro Cecchini Date: Tue, 8 Sep 2026 12:30:38 +0200 Subject: [PATCH 1/4] docs: plan reproducible candidate CI validation --- plans/2026-09-08-ci-validation.md | 86 +++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 plans/2026-09-08-ci-validation.md diff --git a/plans/2026-09-08-ci-validation.md b/plans/2026-09-08-ci-validation.md new file mode 100644 index 0000000..71272a8 --- /dev/null +++ b/plans/2026-09-08-ci-validation.md @@ -0,0 +1,86 @@ +# Make CI reproduce the release support contract + + +Maintain this ExecPlan according to `PLANS.md`. This focused CI PR is stacked on integration draft #22 and implements M7. Documentation formatting and publication authorization remain separate PRs. + +## Purpose / Big Picture + + +Maintainers need a single reproducible validation workflow that tests the candidate on supported interpreters and platforms, fails when expected backends/checkers are missing, and cannot report success after a required job fails or is skipped. CPU jobs should install CPU Torch. Local hooks and hosted checks should execute the same maintained tools. + +## Progress + + +- [x] (2026-09-08) Created matching branch/worktree from the combined candidate. +- [ ] Open early draft PR and inspect current tool configuration. +- [ ] Consolidate PR, push and nightly validation with explicit runtime/checker matrices. +- [ ] Enforce expected backend versions/imports and CPU Torch in tox. +- [ ] Run complete hooks and actionlint in CI; pin actions and maintain update configuration. +- [ ] Validate local commands, hosted Linux/macOS/Windows jobs and final gate behavior. +- [ ] Record evidence and obtain user validation before merge. + +## Surprises & Discoveries + + +The root uv source mapping does not control tox's uv pip installs, so Linux CPU compatibility lanes currently install CUDA Torch dependencies. The current PR workflow duplicates typing runs, differs from nightly, lacks a final required gate, and runs only a subset of local hooks. Docs deployment installs default development dependency groups despite its docs-only purpose. The installed uv supports UV_TORCH_BACKEND=cpu; validate that tox forwards it before claiming CPU-only behavior. + +## Decision Log + + +Decision: Keep one reusable validation workflow used by PR/push and nightly, accepting a ref for later immutable release validation. Rationale: shared checks prevent publishing from relying on a weaker, separate test path. Default caller ref remains the exact event SHA. Date: 2026-09-08. + +Decision: Exercise current locked CPU backends on Python 3.10–3.14 under Linux and both endpoints on macOS/Windows, subject to demonstrated backend wheel availability. Keep explicit dependency/checker floor lanes on Linux. Rationale: checkers and backends have interpreter-specific wheels; cross-platform claims require actual jobs. + +Decision: Make missing expected backends and non-CPU Torch fatal in their CPU lanes. Rationale: importorskip remains useful for optional local environments but cannot establish a required support lane. + +Decision: Use full commit pins for external actions with a maintained update configuration. Run the locked prek suite plus actionlint, and fail if formatting/locking changed tracked files. Rationale: local and hosted tool drift already caused failures during the audit. + +## Outcomes & Retrospective + + +Implementation pending. A successful CPU matrix does not prove CuPy GPU execution or resolve the native-union integration blocker. + +## Context and Orientation + + +Worktree `/Users/ale/Code/bearshape-worktrees/ci-validation`, branch `codex/ci-validation`, base integration `b2a1df0`. `.github/workflows/ci.yml` and `nightly.yml` duplicate matrices. `tox.toml` defines candidate/floor factors; `tests/conftest.py` filters optional backend suites. `tests/test_typecheck.py` executes all four checker positive/negative batches. `tools/check_distribution.py` and `tools/smoke_minimal.py` provide independent artifact/minimal checks. + +## Plan of Work + + +Introduce `.github/workflows/validate.yml` with explicit quality, current-runtime, checker, compatibility-floor, docs, distribution/minimal jobs and an always-running required gate. Keep CI/nightly as small callers of this common workflow. Parameterize only the checkout ref needed by release validation, rather than generating matrices in a separate framework. + +Add a small backend preflight command that imports requested modules, reports actual versions/origins, asserts exact beartype and verifies CPU Torch when selected. Reuse it in locked CPU jobs and tox before tests. Configure tox's uv installer with CPU backend selection and inspect generated commands plus Linux package resolution. Preserve absent-backend skips only outside the selected lane. + +Run all normal prek hooks in quality, then manual actionlint. Replace the standalone unpinned pre-push pyright hook with the locked four-checker harness. Keep the existing local pytest pre-push behavior. Pin external actions, add weekly GitHub Actions updates, and limit docs deployment to the locked docs group. Add a changelog/contributor note describing the actual validation commands and support matrix. + +## Concrete Steps + + +Run targeted tox configuration inspection and backend preflight first, then: + + uv run --locked tox run -e dev,py310-bt023rc0-cpu,py314-bt023rc0-cpu + uv run --locked prek run -a + uv run --locked prek run actionlint -a --stage manual + +Push the draft to execute the hosted matrix. Read failed-job logs, fix demonstrated issues, and rerun only affected checks unless changes require broader validation. Test that the final gate accepts only success for every required job result. + +## Validation and Acceptance + + +The current locked suite executes every selected backend and checker. Exact rc0 is asserted. Linux CPU Torch has no CUDA build, macOS/Windows endpoint results are recorded accurately, backend/checker floor jobs pass, distributions/minimal installs pass, docs build passes, and all hooks execute. Required failures/skips/cancellations cannot produce a green final gate. No branch protection or environment settings are changed by this PR. + +## Idempotence and Recovery + + +Use only the isolated worktree and disposable test environments. Workflow edits may consume normal GitHub Actions runs but do not publish, deploy the candidate branch or change administrative settings. Keep main unchanged and request user validation before merge. + +## Artifacts and Notes + + +Record `/Users/ale/Code/bearshape-implementation-2026-09-08/evidence/ci-*` and hosted run/job links. Use actual platform/version results when updating the final support matrix. The combined pre-CI baseline passed 1,074 tests with five skips and 91.62% coverage. + +## Interfaces and Dependencies + + +Runtime dependencies remain beartype and typing_extensions. CI uses the locked uv/prek/checker toolchain, the existing test/backend groups and tox factors. The reusable workflow's checkout-ref input is for validation only and carries no publication permission. From 8f6b0bc0a627e2571e3141378fac5cf1024c1898 Mon Sep 17 00:00:00 2001 From: Alessandro Cecchini Date: Tue, 8 Sep 2026 12:35:04 +0200 Subject: [PATCH 2/4] ci: require shared CPU and checker validation across platforms --- .github/dependabot.yml | 9 ++ .github/workflows/ci.yml | 175 ++----------------------------- .github/workflows/docs.yml | 14 ++- .github/workflows/nightly.yml | 50 +-------- .github/workflows/validate.yml | 184 +++++++++++++++++++++++++++++++++ .gitignore | 1 + .pre-commit-config.yaml | 16 +-- CHANGELOG.md | 3 + CONTRIBUTING.md | 15 +++ tools/validate_runtime.py | 48 +++++++++ tox.toml | 6 +- 11 files changed, 288 insertions(+), 233 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/validate.yml create mode 100644 tools/validate_runtime.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2dedc4d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + github-actions: + patterns: ["*"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47cea37..62b96c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,178 +4,15 @@ on: push: branches: [main] pull_request: + workflow_dispatch: + +permissions: + contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - ruff: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 - - run: uv run --locked --only-group dev ruff check src/ tests/ - - run: uv run --locked --only-group dev ruff format --check --diff src/ tests/ - - spelling: - name: Spell Check with Typos - runs-on: ubuntu-latest - steps: - - name: Checkout Actions Repository - uses: actions/checkout@v6 - - name: Spell Check Repo - uses: crate-ci/typos@v1.45.0 - - distributions: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 - - run: uv build --sdist - - run: uv build dist/*.tar.gz --wheel - - run: uv run --locked --only-group dev python tools/check_distribution.py dist/*.whl dist/*.tar.gz - - minimal-install: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.10", "3.14"] - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 - - run: uv build --wheel - - run: uv venv --python "${{ matrix.python-version }}" .minimal - - run: uv pip install --python .minimal/bin/python dist/*.whl "beartype==0.23.0rc0" - - run: .minimal/bin/python -I tools/smoke_minimal.py - - typecheck: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 - with: - enable-cache: true - - run: uv run --locked pytest tests/test_typecheck.py -v - - typecheck-compat: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 - with: - enable-cache: true - - run: uv run --locked --python "${{ matrix.python-version }}" pytest tests/test_typecheck.py -v - - # ----------------------------------------------------------------------- - # Test — always run (locked dev env) - # ----------------------------------------------------------------------- - - test: - needs: [ruff, spelling, typecheck] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 - - run: uv run --locked tox run -e dev - - # ----------------------------------------------------------------------- - # PR: fast compat matrix - # ----------------------------------------------------------------------- - - compat-pr: - if: github.event_name == 'pull_request' - needs: [test] - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { python: py310, beartype: bt023rc0, backend: cpu } - - { python: py314, beartype: bt023rc0, backend: cpu } - - { python: py310, beartype: bt023rc0, backend: numpy22 } - - { python: py310, beartype: bt023rc0, backend: jax05 } - - { python: py310, beartype: bt023rc0, backend: torch26 } - - { python: py310, beartype: bt023rc0, backend: optree014 } - - { python: py313, beartype: bt023rc0, backend: numpy24 } - - { python: py313, beartype: bt023rc0, backend: jax09 } - - { python: py313, beartype: bt023rc0, backend: torch210 } - - { python: py313, beartype: bt023rc0, backend: optree019 } - name: ${{ matrix.python }}-${{ matrix.beartype }}-${{ matrix.backend }} - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v7 - - run: uv run --locked tox run -e "${{ matrix.python }}-${{ matrix.beartype }}-${{ matrix.backend }}" - - typecheck-pr: - if: github.event_name == 'pull_request' - needs: [test] - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { env: py313-bt023rc0-type-pyright1408 } - - { env: py313-bt023rc0-type-mypy119 } - - { env: py313-bt023rc0-type-ty } - - { env: py313-bt023rc0-type-pyrefly } - name: ${{ matrix.env }} - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v7 - - run: uv run --locked tox run -e "${{ matrix.env }}" - - # ----------------------------------------------------------------------- - # Push: medium compat matrix - # ----------------------------------------------------------------------- - - compat-push: - if: github.event_name == 'push' - needs: [test] - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { python: py310, beartype: bt023rc0, backend: cpu } - - { python: py314, beartype: bt023rc0, backend: cpu } - - { python: py310, beartype: bt023rc0, backend: numpy22 } - - { python: py310, beartype: bt023rc0, backend: jax05 } - - { python: py310, beartype: bt023rc0, backend: torch26 } - - { python: py310, beartype: bt023rc0, backend: optree014 } - - { python: py312, beartype: bt023rc0, backend: numpy24 } - - { python: py312, beartype: bt023rc0, backend: jax09 } - - { python: py312, beartype: bt023rc0, backend: torch210 } - - { python: py312, beartype: bt023rc0, backend: optree019 } - - { python: py313, beartype: bt023rc0, backend: numpy24 } - - { python: py313, beartype: bt023rc0, backend: jax09 } - - { python: py313, beartype: bt023rc0, backend: torch210 } - - { python: py313, beartype: bt023rc0, backend: optree019 } - name: ${{ matrix.python }}-${{ matrix.beartype }}-${{ matrix.backend }} - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v7 - - run: uv run --locked tox run -e "${{ matrix.python }}-${{ matrix.beartype }}-${{ matrix.backend }}" - - typecheck-push: - if: github.event_name == 'push' - needs: [test] - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { env: py313-bt023rc0-type-pyright1408 } - - { env: py313-bt023rc0-type-mypy119 } - - { env: py313-bt023rc0-type-ty } - - { env: py313-bt023rc0-type-pyrefly } - name: ${{ matrix.env }} - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v7 - - run: uv run --locked tox run -e "${{ matrix.env }}" + validate: + uses: ./.github/workflows/validate.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 11231e7..805212b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,14 +20,12 @@ jobs: env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b - - uses: actions/setup-python@v5 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 with: - python-version: "3.12" - - run: uv sync --group docs --no-dev - - run: uv run zensical build --clean - - uses: actions/upload-pages-artifact@v4 + version: "0.11.17" + - run: uv run --locked --only-group docs zensical build --clean + - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 with: path: site @@ -41,4 +39,4 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a8a4419..a9291af 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -5,53 +5,13 @@ on: - cron: "0 4 * * *" workflow_dispatch: +permissions: + contents: read + concurrency: group: ${{ github.workflow }} cancel-in-progress: true jobs: - # ----------------------------------------------------------------------- - # Full tox matrix — oldest + newest of each axis - # ----------------------------------------------------------------------- - - compat-nightly: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { python: py310, beartype: bt023rc0, backend: cpu } - - { python: py314, beartype: bt023rc0, backend: cpu } - - { python: py310, beartype: bt023rc0, backend: numpy22 } - - { python: py310, beartype: bt023rc0, backend: jax05 } - - { python: py310, beartype: bt023rc0, backend: torch26 } - - { python: py310, beartype: bt023rc0, backend: optree014 } - - { python: py313, beartype: bt023rc0, backend: numpy24 } - - { python: py313, beartype: bt023rc0, backend: jax09 } - - { python: py313, beartype: bt023rc0, backend: torch210 } - - { python: py313, beartype: bt023rc0, backend: optree019 } - name: ${{ matrix.python }}-${{ matrix.beartype }}-${{ matrix.backend }} - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v7 - - run: uv run --locked tox run -e "${{ matrix.python }}-${{ matrix.beartype }}-${{ matrix.backend }}" - - # ----------------------------------------------------------------------- - # All typechecker versions - # ----------------------------------------------------------------------- - - typecheck-nightly: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { env: py313-bt023rc0-type-pyright1408 } - - { env: py313-bt023rc0-type-mypy119 } - - { env: py313-bt023rc0-type-ty } - - { env: py313-bt023rc0-type-pyrefly } - name: ${{ matrix.env }} - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v7 - - run: uv run --locked tox run -e "${{ matrix.env }}" + validate: + uses: ./.github/workflows/validate.yml diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..3b4ff5b --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,184 @@ +name: Validate candidate + +on: + workflow_call: + inputs: + ref: + description: Exact commit to validate; defaults to the caller event SHA + type: string + default: "" + +permissions: + contents: read + +defaults: + run: + shell: bash + +env: + UV_TORCH_BACKEND: cpu + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 + with: + version: "0.11.17" + - run: uv run --locked --only-group dev prek run -a + - run: uv run --locked --only-group dev prek run actionlint -a --stage manual + - run: git diff --exit-code + + runtime: + name: Runtime (${{ matrix.os }}, ${{ matrix.python }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + include: + - { os: macos-latest, python: "3.10" } + - { os: macos-latest, python: "3.14" } + - { os: windows-latest, python: "3.10" } + - { os: windows-latest, python: "3.14" } + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 + with: + version: "0.11.17" + python-version: ${{ matrix.python }} + enable-cache: true + - run: uv sync --locked --no-default-groups --group optional --group test + - run: uv run --no-sync python tools/validate_runtime.py cpu + - run: >- + uv run --no-sync pytest tests/ --ignore=tests/test_typecheck.py -n 4 + --cov=bearshape --cov-branch --cov-report=term-missing:skip-covered + --cov-fail-under=90 + + checkers: + name: Four checkers (Python ${{ matrix.python }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 + with: + version: "0.11.17" + python-version: ${{ matrix.python }} + enable-cache: true + - run: uv sync --locked --no-default-groups --group optional --group static --group test + - run: uv run --no-sync python tools/validate_runtime.py type + - run: uv run --no-sync pytest tests/test_typecheck.py -v + + compatibility: + name: Compatibility (${{ matrix.environment }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + environment: + - py310-bt023rc0-numpy22 + - py310-bt023rc0-jax05 + - py310-bt023rc0-torch26 + - py310-bt023rc0-optree014 + - py313-bt023rc0-numpy24 + - py313-bt023rc0-jax09 + - py313-bt023rc0-torch210 + - py313-bt023rc0-optree019 + - py313-bt023rc0-type-pyright1408 + - py313-bt023rc0-type-mypy119 + - py313-bt023rc0-type-ty + - py313-bt023rc0-type-pyrefly + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 + with: + version: "0.11.17" + enable-cache: true + - run: uv run --locked --only-group test tox run -e "${{ matrix.environment }}" + + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 + with: + version: "0.11.17" + - run: uv run --locked --only-group docs zensical build --clean + + distributions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 + with: + version: "0.11.17" + - run: uv build --sdist + - run: uv build dist/*.tar.gz --wheel + - run: uv run --locked --only-group dev python tools/check_distribution.py dist/*.whl dist/*.tar.gz + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: candidate-distributions + path: dist/ + if-no-files-found: error + + minimal: + name: Minimal wheel (Python ${{ matrix.python }}) + needs: distributions + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python: ["3.10", "3.14"] + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v7 + with: + version: "0.11.17" + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: candidate-distributions + path: dist/ + - run: uv venv --python "${{ matrix.python }}" .minimal + - run: uv pip install --python .minimal/bin/python dist/*.whl "beartype==0.23.0rc0" + - run: .minimal/bin/python -I tools/smoke_minimal.py + + required: + name: Required validation + if: always() + needs: + [quality, runtime, checkers, compatibility, docs, distributions, minimal] + runs-on: ubuntu-latest + steps: + - name: Require every validation job to succeed + env: + RESULTS: ${{ toJSON(needs) }} + run: | + python - <<'PY' + import json + import os + results = json.loads(os.environ["RESULTS"]) + failed = {name: job["result"] for name, job in results.items() if job["result"] != "success"} + if failed: + raise SystemExit(f"Required validation did not succeed: {failed}") + print("All required validation jobs succeeded") + PY diff --git a/.gitignore b/.gitignore index 5378d50..42b6afc 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ wheels/ !.claude/commands/ .vscode/ .coverage +.coverage.* site/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 96442fe..bc130b8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -118,19 +118,19 @@ repos: - id: shellcheck priority: 50 - - repo: https://github.com/RobertCraigie/pyright-python - rev: v1.1.408 + - repo: local hooks: - - id: pyright - stages: [pre-push] + - id: typecheck + name: four-checker conformance + entry: uv run --locked pytest tests/test_typecheck.py -q + language: system pass_filenames: false + always_run: true + stages: [pre-push] priority: 60 - - - repo: local - hooks: - id: pytest name: pytest - entry: uv run --locked pytest tests/ -n auto -x -q + entry: uv run --locked pytest tests/ --ignore=tests/test_typecheck.py -n auto -x -q language: system pass_filenames: false always_run: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a9165..d78a80a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project follows ## [Unreleased] +- Share candidate validation across PRs, pushes and nightly runs, with explicit + CPU backends, four checkers, complete hooks and a required final gate. + - Accept ordinary convertible inputs in static Like annotations: NumPy numeric casting families and JAX/Torch scalars, arrays and nested sequences. Preserve native result types after explicit conversion. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bfc7e15..aa09c43 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,3 +77,18 @@ metadata, inline typing information, matching package contents, and downstream test inputs. It prints artifact hashes for release evidence. The source archive includes the tests, lockfile and configuration needed to run `uv sync --locked` and `uv run --locked pytest`. CuPy still requires a separate CUDA environment. + +## Candidate validation + +CI and nightly use `.github/workflows/validate.yml`. Current locked CPU backends +run on Python 3.10–3.14 on Linux, with endpoint jobs on macOS and Windows. +Four-checker consumer tests run on every supported Python on Linux. Separate tox +jobs exercise backend and checker floors with exact beartype 0.23.0rc0. +`tools/validate_runtime.py` fails if an expected backend is absent or Torch is a +CUDA/ROCm build in a CPU lane. Optional local skips do not establish support. + +Use `uv run --locked prek run -a` for the normal hooks and +`uv run --locked prek run actionlint -a --stage manual` for workflow checks. +Pre-push checks use the locked four-checker harness and runtime suite. The +required CI gate accepts only successful completion of every required job. CuPy +GPU validation remains a separate hardware-backed requirement. diff --git a/tools/validate_runtime.py b/tools/validate_runtime.py new file mode 100644 index 0000000..0e68288 --- /dev/null +++ b/tools/validate_runtime.py @@ -0,0 +1,48 @@ +"""Fail early when a required candidate backend is missing or incorrectly built.""" + +from __future__ import annotations + +import argparse +import importlib +import json +import sys + +_BACKENDS = ("numpy", "jax", "torch", "optree") + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("environment", nargs="?", default="cpu") + args = parser.parse_args() + factors = args.environment.split("-") + if any(factor in {"cpu", "dev", "type"} for factor in factors): + backends = _BACKENDS + else: + backends = tuple( + name for name in _BACKENDS if any(f.startswith(name) for f in factors) + ) + if not backends: + parser.error(f"no expected backend in {args.environment!r}") + + versions = {} + for name in ("bearshape", "beartype", *backends): + module = importlib.import_module(name) + versions[name] = { + "version": module.__version__, + "path": module.__file__, + } + if name == "beartype" and module.__version__ != "0.23.0rc0": + message = f"expected beartype 0.23.0rc0, found {module.__version__}" + raise SystemExit(message) + if name == "torch" and ( + module.version.cuda is not None or module.version.hip is not None + ): + message = "CPU validation requires a CPU-only Torch build" + raise SystemExit(message) + sys.stdout.write( + json.dumps({"python": sys.version, "modules": versions}, indent=2) + "\n" + ) + + +if __name__ == "__main__": + main() diff --git a/tox.toml b/tox.toml index 5ac739c..44b423a 100644 --- a/tox.toml +++ b/tox.toml @@ -30,6 +30,7 @@ env_list = [ [env_run_base] runner = "uv-venv-runner" description = "{env_name}" +set_env = { UV_TORCH_BACKEND = "cpu" } deps = [ "pytest>=9.0.2", "pytest-xdist>=3.5.0", @@ -90,8 +91,7 @@ deps = [ commands = [["pytest", "tests/", "-n", "auto", { replace = "posargs", default = [], extend = true }]] commands_pre = [ ["python", "tools/validate_tox_env.py", "{env_name}"], - ["python", "-c", "import beartype; print('beartype', beartype.__version__); assert beartype.__version__ == '0.23.0rc0'"], - { replace = "if", condition = "factor.cpu", then = [["python", "-c", "import numpy, jax, torch, optree; print([(m.__name__, m.__version__) for m in (numpy, jax, torch, optree)])"]], else = [], extend = true }, + ["python", "tools/validate_runtime.py", "{env_name}"], ] # --------------------------------------------------------------------------- @@ -101,7 +101,7 @@ commands_pre = [ [env.dev] runner = "uv-venv-lock-runner" dependency_groups = ["dev", "optional", "static", "test"] -commands_pre = [] +commands_pre = [["python", "tools/validate_runtime.py", "dev"]] commands = [ [ "pytest", From 688e669f1745f5e2dfc5a4c2672a3080b4c001b7 Mon Sep 17 00:00:00 2001 From: Alessandro Cecchini Date: Tue, 8 Sep 2026 12:46:18 +0200 Subject: [PATCH 3/4] ci: honor the selected checker interpreter environment --- plans/2026-09-08-ci-validation.md | 2 ++ pyproject.toml | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plans/2026-09-08-ci-validation.md b/plans/2026-09-08-ci-validation.md index 71272a8..f61a7c4 100644 --- a/plans/2026-09-08-ci-validation.md +++ b/plans/2026-09-08-ci-validation.md @@ -84,3 +84,5 @@ Record `/Users/ale/Code/bearshape-implementation-2026-09-08/evidence/ci-*` and h Runtime dependencies remain beartype and typing_extensions. CI uses the locked uv/prek/checker toolchain, the existing test/backend groups and tox factors. The reusable workflow's checkout-ref input is for validation only and carries no publication permission. + +Revision note — 2026-09-08: All nine hosted platform runtime lanes, current checker lanes, backend floors, docs, quality and artifacts passed. The pyright floor failed because hard-coded venvPath/venv overrode its explicit tox interpreter. Reproduced locally only after exact-syncing the driver to the test-only group; inexact uv run had retained backend packages and hidden the defect. Removed the redundant fixed environment selection, retaining explicit harness interpreter paths. diff --git a/pyproject.toml b/pyproject.toml index 8fe5117..4a2d6a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,8 +110,6 @@ reportUnusedImport = "warning" # Check the oldest supported Python, not the interpreter running the checker. pythonVersion = "3.10" pythonPlatform = "Linux" -venvPath = "." -venv = ".venv" [tool.pyrefly] preset = "default" From f396e728d857cee1115b6c243e0c0163dac18354 Mon Sep 17 00:00:00 2001 From: Alessandro Cecchini Date: Tue, 8 Sep 2026 12:51:24 +0200 Subject: [PATCH 4/4] docs: record complete cross-platform CI validation --- plans/2026-09-08-ci-validation.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plans/2026-09-08-ci-validation.md b/plans/2026-09-08-ci-validation.md index f61a7c4..fcea827 100644 --- a/plans/2026-09-08-ci-validation.md +++ b/plans/2026-09-08-ci-validation.md @@ -12,12 +12,13 @@ Maintainers need a single reproducible validation workflow that tests the candid - [x] (2026-09-08) Created matching branch/worktree from the combined candidate. -- [ ] Open early draft PR and inspect current tool configuration. -- [ ] Consolidate PR, push and nightly validation with explicit runtime/checker matrices. -- [ ] Enforce expected backend versions/imports and CPU Torch in tox. -- [ ] Run complete hooks and actionlint in CI; pin actions and maintain update configuration. -- [ ] Validate local commands, hosted Linux/macOS/Windows jobs and final gate behavior. -- [ ] Record evidence and obtain user validation before merge. +- [x] (2026-09-08) Opened PR #23 and inspected actual uv/tox installer configuration. +- [x] (2026-09-08) Added shared workflow with nine platform runtime lanes, five current checker lanes and twelve floor lanes. +- [x] (2026-09-08) Backend preflight asserts exact rc0 and CPU Torch; tox forwards UV_TORCH_BACKEND=cpu. +- [x] (2026-09-08) Full hooks/actionlint run in CI; validation/docs actions pinned with weekly update configuration. Publication action changes remain in M9. +- [x] (2026-09-08) All hosted required jobs pass at 688e669; local dev and candidate endpoints pass; gate probes reject failure, skip and cancellation. +- [x] (2026-09-08) Recorded implementation, hosted results and the reproduced pyright environment defect. +- [ ] Obtain user validation before merge. ## Surprises & Discoveries @@ -38,7 +39,7 @@ Decision: Use full commit pins for external actions with a maintained update con ## Outcomes & Retrospective -Implementation pending. A successful CPU matrix does not prove CuPy GPU execution or resolve the native-union integration blocker. +All 32 required hosted jobs pass at implementation commit 688e669, including the aggregate gate, plus the external secret scan. Local development tests pass 1,074 cases with five documented skips and 91.62% coverage; exact-candidate CPU endpoint lanes each pass 1,066 runtime tests. The narrower tox-driver install exposed pyright environment routing that a populated developer environment hid; removing the fixed venv selection makes the selected interpreter authoritative. A successful CPU matrix does not prove CuPy GPU execution or resolve the native-union integration blocker. ## Context and Orientation