diff --git a/.github/workflows/python-coverage-pilot.yml b/.github/workflows/python-coverage-pilot.yml new file mode 100644 index 000000000..2460ac150 --- /dev/null +++ b/.github/workflows/python-coverage-pilot.yml @@ -0,0 +1,85 @@ +name: Python coverage pilot + +on: + pull_request: + branches: + - master + paths: + - ".github/workflows/python-coverage-pilot.yml" + - "backend/domain/text_identity.py" + - "backend/research_markup.py" + - "tests/test_text_identity.py" + - "tests/fixtures/text_identity_vectors.json" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: python-coverage-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + text-identity-coverage: + name: Python line + branch coverage (informational) + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: "3.12" + + - name: Install pinned coverage.py + run: >- + python -m pip install --isolated --disable-pip-version-check + "--only-binary=:all:" "coverage==7.16.2" + + # B1 / #651: bounded pilot on existing, pure-Python test vectors. + # This is NOT whole-backend coverage and has no fail-under threshold. + # Keep the existing full PRKS Test Gate unchanged (#180 guard). + - name: Measure text identity line and branch coverage + shell: bash + run: | + set -euo pipefail + python -m coverage run --branch --source=backend.domain.text_identity \ + -m unittest discover -s tests -p test_text_identity.py + python -m coverage report --show-missing + python -m coverage json -o "$RUNNER_TEMP/prks-coverage.json" + python - <<'PY' + import json + import os + from pathlib import Path + + totals = json.loads( + (Path(os.environ["RUNNER_TEMP"]) / "prks-coverage.json").read_text( + encoding="utf-8" + ) + )["totals"] + lines = totals["num_statements"] + branches = totals["num_branches"] + line_rate = ( + f'{100 * totals["covered_lines"] / lines:.1f}%' if lines else "N/A" + ) + branch_rate = ( + f'{100 * totals["covered_branches"] / branches:.1f}%' + if branches + else "N/A" + ) + summary = ( + "## Python coverage pilot (text identity only)\n\n" + f'- Lines: {totals["covered_lines"]}/{lines} ({line_rate})\n' + f'- Branches: {totals["covered_branches"]}/{branches} ({branch_rate})\n\n' + "Informational only; no threshold. This is a focused module, " + "not overall Python/backend coverage. Raw coverage data stays " + "on the runner and is not uploaded.\n" + ) + print(summary) + with Path(os.environ["GITHUB_STEP_SUMMARY"]).open("a", encoding="utf-8") as out: + out.write(summary) + PY