From 5980f099e2d857be08bacb8dae0439052aa1c7af Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 14:34:27 +0900 Subject: [PATCH 1/2] refactor(ci): extract shared quality-gate reusable workflow for 2 of 8 duplicated files An audit of the 8 .github/workflows/*-quality-ci.yml files that share a bootstrap-templated skeleton found only one pair -- javascript-coverage-quality-ci.yml and organization-commercial-readiness-loop-quality-ci.yml -- where the shared shape (exact-head checkout, an identical pinned six-package requirements heredoc, coverage run --branch + --fail-under=100, compileall, git diff --exit-code) was genuinely the same logic, differing only in timeout, pytest target, and coverage --include path. Extract that into a new workflow_call-only exact-head-coverage-quality-gate.yml and turn both callers into thin uses:/with: wrappers. Verified first that no branch-protection required status check or the org's required-workflow ruleset references either caller's job name, so restructuring them is safe. Updated the contract tests that pinned the old inline text and added one for the new gate's own contract and both callers' input wiring. The other 6 files each encode a genuinely different policy (harden-runner presence, a docstring gate, exact-head-verification mechanics, multi-Python-version matrices with non-shared extra logic, or no coverage --fail-under step at all) so templatizing them would weaken what they individually enforce. Left untouched. Co-Authored-By: Claude Sonnet 5 --- .../exact-head-coverage-quality-gate.yml | 81 +++++++++++++++++++ .../javascript-coverage-quality-ci.yml | 54 +++---------- ...n-commercial-readiness-loop-quality-ci.yml | 59 +++----------- CHANGELOG.md | 37 +++++++++ ...act_head_coverage_quality_gate_contract.py | 80 ++++++++++++++++++ ...mmercial_readiness_loop_import_contract.py | 6 +- ...zation_commercial_readiness_loop_policy.py | 18 ++++- 7 files changed, 238 insertions(+), 97 deletions(-) create mode 100644 .github/workflows/exact-head-coverage-quality-gate.yml create mode 100644 tests/test_exact_head_coverage_quality_gate_contract.py diff --git a/.github/workflows/exact-head-coverage-quality-gate.yml b/.github/workflows/exact-head-coverage-quality-gate.yml new file mode 100644 index 0000000000..675fed884a --- /dev/null +++ b/.github/workflows/exact-head-coverage-quality-gate.yml @@ -0,0 +1,81 @@ +name: Exact-Head Coverage Quality Gate + +# Reusable workflow_call gate shared by quality-CI callers that measure one +# scripts/ci module at 100% branch coverage against the exact PR head SHA. +# Callers: javascript-coverage-quality-ci.yml, +# organization-commercial-readiness-loop-quality-ci.yml. +# +# Not every quality-CI workflow under .github/workflows/ fits this shape — +# harden-runner presence, docstring gates, exact-head verification mechanics, +# and multi-python-version matrices differ enough across the others +# (agent-mention-router, exact-artifact-sbom-attestation, noema-token-lifetime, +# opencode-rust-coverage-toolchain, strix-changed-path, trusted-uv-materializer) +# that forcing them into this same template would either weaken what they +# enforce or need enough per-caller toggles to defeat the point of sharing. + +on: + workflow_call: + inputs: + timeout_minutes: + description: Job timeout in minutes + required: true + type: number + pytest_target: + description: pytest path or glob to run under coverage + required: true + type: string + coverage_include: + description: Single scripts/ci module path passed to `coverage report --include` + required: true + type: string + compileall_targets: + description: Space-separated file list passed to `python -m compileall -q` + required: true + type: string + +permissions: + contents: read + +jobs: + quality-gate: + runs-on: ubuntu-24.04 + timeout-minutes: ${{ inputs.timeout_minutes }} + steps: + - name: Checkout exact source revision + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.14' + - name: Install exact hash-verified quality dependencies + env: + PIP_DISABLE_PIP_VERSION_CHECK: '1' + PIP_NO_INPUT: '1' + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + cat >"${RUNNER_TEMP}/exact-head-coverage-quality-gate-requirements.txt" <<'EOF' + coverage==7.15.2 --hash=sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f + iniconfig==2.1.0 --hash=sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760 + packaging==26.2 --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e + pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 + pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 + pytest==9.1.1 --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c + EOF + python -m pip install \ + --only-binary=:all: \ + --require-hashes \ + -r "${RUNNER_TEMP}/exact-head-coverage-quality-gate-requirements.txt" + - name: Verify exact-head policy and full branch coverage + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}" + python -m coverage run --branch -m pytest --import-mode=importlib ${{ inputs.pytest_target }} -q + python -m coverage report \ + --include='${{ inputs.coverage_include }}' \ + --show-missing \ + --fail-under=100 + python -m compileall -q ${{ inputs.compileall_targets }} + git diff --exit-code diff --git a/.github/workflows/javascript-coverage-quality-ci.yml b/.github/workflows/javascript-coverage-quality-ci.yml index d9c8c74299..44b3c8d335 100644 --- a/.github/workflows/javascript-coverage-quality-ci.yml +++ b/.github/workflows/javascript-coverage-quality-ci.yml @@ -5,6 +5,7 @@ on: branches: [main] paths: - '.github/workflows/javascript-coverage-quality-ci.yml' + - '.github/workflows/exact-head-coverage-quality-gate.yml' - 'scripts/ci/javascript_coverage_gate.py' - 'tests/test_javascript_coverage_gate.py' - 'tests/test_javascript_coverage_storybook_boundary.py' @@ -17,47 +18,12 @@ concurrency: jobs: exact-head-coverage-contract: - runs-on: ubuntu-24.04 - timeout-minutes: 15 - steps: - - name: Checkout exact source revision - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: '3.14' - - name: Install exact hash-verified quality dependencies - env: - PIP_DISABLE_PIP_VERSION_CHECK: '1' - PIP_NO_INPUT: '1' - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - cat >"${RUNNER_TEMP}/javascript-coverage-quality-requirements.txt" <<'EOF' - coverage==7.15.2 --hash=sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f - iniconfig==2.1.0 --hash=sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760 - packaging==26.2 --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e - pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 - pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 - pytest==9.1.1 --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c - EOF - python -m pip install \ - --only-binary=:all: \ - --require-hashes \ - -r "${RUNNER_TEMP}/javascript-coverage-quality-requirements.txt" - - name: Verify full central suite and classifier coverage - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}" - python -m coverage run --branch -m pytest --import-mode=importlib tests -q - python -m coverage report \ - --include='scripts/ci/javascript_coverage_gate.py' \ - --show-missing \ - --fail-under=100 - python -m compileall -q \ - scripts/ci/javascript_coverage_gate.py \ - tests/test_javascript_coverage_gate.py \ - tests/test_javascript_coverage_storybook_boundary.py - git diff --exit-code + uses: ./.github/workflows/exact-head-coverage-quality-gate.yml + with: + timeout_minutes: 15 + pytest_target: tests + coverage_include: scripts/ci/javascript_coverage_gate.py + compileall_targets: >- + scripts/ci/javascript_coverage_gate.py + tests/test_javascript_coverage_gate.py + tests/test_javascript_coverage_storybook_boundary.py diff --git a/.github/workflows/organization-commercial-readiness-loop-quality-ci.yml b/.github/workflows/organization-commercial-readiness-loop-quality-ci.yml index 50729db472..2e892d024e 100644 --- a/.github/workflows/organization-commercial-readiness-loop-quality-ci.yml +++ b/.github/workflows/organization-commercial-readiness-loop-quality-ci.yml @@ -6,6 +6,7 @@ on: paths: - ".github/workflows/organization-commercial-readiness-loop.yml" - ".github/workflows/organization-commercial-readiness-loop-quality-ci.yml" + - ".github/workflows/exact-head-coverage-quality-gate.yml" - "scripts/ci/organization_commercial_readiness_loop.py" - "organization_commercial_readiness_fixtures.py" - "tests/test_organization_commercial_readiness_loop*.py" @@ -21,52 +22,12 @@ concurrency: jobs: exact-head-policy: - runs-on: ubuntu-24.04 - timeout-minutes: 10 - steps: - - name: Checkout exact source revision - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.event.pull_request.head.sha }} - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - - - name: Install exact hash-verified quality dependencies - env: - PIP_DISABLE_PIP_VERSION_CHECK: "1" - PIP_NO_INPUT: "1" - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - cat >"${RUNNER_TEMP}/organization-loop-quality-requirements.txt" <<'EOF' - coverage==7.15.2 --hash=sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f - iniconfig==2.1.0 --hash=sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760 - packaging==26.2 --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e - pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 - pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 - pytest==9.1.1 --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c - EOF - python -m pip install \ - --only-binary=:all: \ - --require-hashes \ - -r "${RUNNER_TEMP}/organization-loop-quality-requirements.txt" - - - name: Prove exact-head policy and full branch coverage - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha }}" - python -m coverage run \ - --branch \ - -m pytest --import-mode=importlib tests/test_organization_commercial_readiness_loop*.py -q - python -m coverage report \ - --include='scripts/ci/organization_commercial_readiness_loop.py' \ - --show-missing \ - --fail-under=100 - python -m compileall -q \ - scripts/ci/organization_commercial_readiness_loop.py \ - organization_commercial_readiness_fixtures.py \ - tests/test_organization_commercial_readiness_loop*.py - git diff --exit-code + uses: ./.github/workflows/exact-head-coverage-quality-gate.yml + with: + timeout_minutes: 10 + pytest_target: "tests/test_organization_commercial_readiness_loop*.py" + coverage_include: scripts/ci/organization_commercial_readiness_loop.py + compileall_targets: >- + scripts/ci/organization_commercial_readiness_loop.py + organization_commercial_readiness_fixtures.py + tests/test_organization_commercial_readiness_loop*.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 552966c233..285c8ee26b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,43 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] +- **Consolidate the two genuinely duplicate quality-CI callers behind one reusable + `workflow_call` gate; leave the other six alone.** An audit of the 8 + `.github/workflows/*-quality-ci.yml` bootstrap-templated files found only one pair — + `javascript-coverage-quality-ci.yml` and + `organization-commercial-readiness-loop-quality-ci.yml` — where the shared skeleton + (checkout at the exact PR head, an identical pinned six-package mini-requirements + heredoc, `coverage run --branch -m pytest --import-mode=importlib`, `coverage report + --fail-under=100`, `compileall`, `git diff --exit-code`) was byte-for-byte the same + logic with only the timeout, pytest target, and coverage `--include` path varying per + subsystem. Extracted that shared shape into a new + `.github/workflows/exact-head-coverage-quality-gate.yml` reusable workflow + (`workflow_call`-only, four required inputs: `timeout_minutes`, `pytest_target`, + `coverage_include`, `compileall_targets`) and turned both callers into thin + `uses:`/`with:` wrappers. Verified first that no branch-protection required status + check or the org's required-workflow ruleset references either caller's job name + (`exact-head-coverage-contract` / `exact-head-policy`) before restructuring, so nothing + downstream depends on their exact shape. Updated the three contract tests that pinned + the old inline text + (`test_organization_commercial_readiness_loop_policy.py`, + `test_organization_commercial_readiness_loop_import_contract.py`) to check the + coverage/exact-head mechanics against the shared gate file and the subsystem wiring + against each caller, and added + `tests/test_exact_head_coverage_quality_gate_contract.py` to pin the gate's own + `workflow_call` contract and both callers' input wiring. The other 6 files + (`agent-mention-router-quality-ci.yml`, `exact-artifact-sbom-attestation-quality.yml`, + `noema-token-lifetime-quality-ci.yml`, + `opencode-rust-coverage-toolchain-quality-ci.yml`, `strix-changed-path-quality-ci.yml`, + `trusted-uv-materializer-quality-ci.yml`) look superficially similar but each encodes a + genuinely different policy -- harden-runner presence, a docstring/interrogate gate, + exact-head-verification mechanics (or, for noema, no `ref:` pin at all), multi-Python- + version matrices with non-shared extra logic (a tomli-fallback exercise, a Python 3.10 + compile-only contract), or no `coverage --fail-under` step at all (strix delegates to a + bash gate script instead) -- so templatizing them would either weaken what they + individually enforce or need enough per-caller toggles to defeat the point of sharing. + Left untouched, matching the precedent already set for ruling out the agent-mention + dispatch pair and the noema/opencode/strix "cancel superseded runs" jobs. Full suite: + 2603 passed, 1 skipped, 100% branch coverage, 100% docstrings, `actionlint` clean. - **Fix stale test assertions and dead-code gaps left by `#1654`, `#1656`, and `#1658`.** Reproduced all failures on a fresh unmodified `main` clone before attributing blame. `#1654` (introducing `scripts/ci/current_head_run_coalescer.py` and hardening several diff --git a/tests/test_exact_head_coverage_quality_gate_contract.py b/tests/test_exact_head_coverage_quality_gate_contract.py new file mode 100644 index 0000000000..d17df296b6 --- /dev/null +++ b/tests/test_exact_head_coverage_quality_gate_contract.py @@ -0,0 +1,80 @@ +"""Contracts for the shared exact-head coverage quality-gate reusable workflow.""" + +from __future__ import annotations + +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +GATE_WORKFLOW = ROOT / ".github/workflows/exact-head-coverage-quality-gate.yml" +JS_CALLER = ROOT / ".github/workflows/javascript-coverage-quality-ci.yml" +ORG_LOOP_CALLER = ( + ROOT / ".github/workflows/organization-commercial-readiness-loop-quality-ci.yml" +) + + +def _text(path: Path) -> str: + assert path.is_file(), f"missing workflow file: {path}" + return path.read_text(encoding="utf-8") + + +def test_gate_is_call_only_with_required_string_and_number_inputs() -> None: + """The shared gate accepts no direct trigger and pins every input as required.""" + workflow = _text(GATE_WORKFLOW) + header = workflow.split("\npermissions:\n", 1)[0] + + assert re.search(r"(?m)^ workflow_call:\s*$", header) + for forbidden_trigger in ("pull_request", "push", "schedule", "workflow_dispatch"): + assert not re.search(rf"(?m)^ {forbidden_trigger}:\s*$", header) + + required_inputs = { + "timeout_minutes": "number", + "pytest_target": "string", + "coverage_include": "string", + "compileall_targets": "string", + } + for input_name, input_type in required_inputs.items(): + input_match = re.search( + rf"(?ms)^ {re.escape(input_name)}:\n(?P(?:^ .*\n)+)", + header, + ) + assert input_match is not None, f"missing workflow input: {input_name}" + body = input_match.group("body") + assert re.search(r"(?m)^ required: true\s*$", body) + assert re.search(rf"(?m)^ type: {input_type}\s*$", body) + + +def test_gate_enforces_exact_head_and_full_branch_coverage() -> None: + """The gate itself carries the exact-head check and the coverage fail-under.""" + workflow = _text(GATE_WORKFLOW) + + assert 'test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}"' in workflow + assert "coverage run --branch -m pytest --import-mode=importlib" in workflow + assert "--fail-under=100" in workflow + assert "git diff --exit-code" in workflow + assert "persist-credentials: false" in workflow + + +def test_javascript_and_organization_loop_callers_wire_distinct_subsystem_inputs() -> None: + """Each caller delegates to the shared gate with its own subsystem scope.""" + js_caller = _text(JS_CALLER) + org_caller = _text(ORG_LOOP_CALLER) + + for caller in (js_caller, org_caller): + assert ( + "uses: ./.github/workflows/exact-head-coverage-quality-gate.yml" in caller + ) + + assert "coverage_include: scripts/ci/javascript_coverage_gate.py" in js_caller + assert "pytest_target: tests" in js_caller + assert "timeout_minutes: 15" in js_caller + + assert ( + "coverage_include: scripts/ci/organization_commercial_readiness_loop.py" + in org_caller + ) + assert ( + 'pytest_target: "tests/test_organization_commercial_readiness_loop*.py"' + in org_caller + ) + assert "timeout_minutes: 10" in org_caller diff --git a/tests/test_organization_commercial_readiness_loop_import_contract.py b/tests/test_organization_commercial_readiness_loop_import_contract.py index 43c3c71acd..4d602235ec 100644 --- a/tests/test_organization_commercial_readiness_loop_import_contract.py +++ b/tests/test_organization_commercial_readiness_loop_import_contract.py @@ -8,13 +8,17 @@ / "workflows" / "organization-commercial-readiness-loop-quality-ci.yml" ) +QUALITY_GATE_WORKFLOW = ( + REPO_ROOT / ".github" / "workflows" / "exact-head-coverage-quality-gate.yml" +) def test_quality_gate_uses_import_stable_test_support() -> None: """Hosted and complete-suite collection must resolve the same helper module.""" source = QUALITY_WORKFLOW.read_text(encoding="utf-8") + gate_source = QUALITY_GATE_WORKFLOW.read_text(encoding="utf-8") - assert "--import-mode=importlib" in source + assert "--import-mode=importlib" in gate_source assert '"organization_commercial_readiness_fixtures.py"' in source assert "tests/organization_commercial_readiness_fixtures.py" not in source assert "--include='scripts/ci/organization_commercial_readiness_loop.py' \\\n -m pytest" not in source diff --git a/tests/test_organization_commercial_readiness_loop_policy.py b/tests/test_organization_commercial_readiness_loop_policy.py index 920f8072f9..d156c0c5e0 100644 --- a/tests/test_organization_commercial_readiness_loop_policy.py +++ b/tests/test_organization_commercial_readiness_loop_policy.py @@ -152,6 +152,9 @@ def test_workflow_and_doctoring_contracts() -> None: ROOT / ".github/workflows/organization-commercial-readiness-loop-quality-ci.yml" ).read_text() + quality_gate = ( + ROOT / ".github/workflows/exact-head-coverage-quality-gate.yml" + ).read_text() doctoring = ( ROOT / "docs/doctoring/organization-commercial-readiness-loop.md" ).read_text() @@ -167,10 +170,19 @@ def test_workflow_and_doctoring_contracts() -> None: assert "COPILOT_GITHUB_TOKEN" not in workflow_source assert "github.run_number" in workflow_source assert "persist-credentials: false" in workflow_source - assert "--branch" in quality and "--fail-under=100" in quality - assert "--import-mode=importlib" in quality + # Coverage/exact-head mechanics live in the shared reusable gate; the + # caller only needs to delegate to it with the right subsystem inputs. + assert ( + "uses: ./.github/workflows/exact-head-coverage-quality-gate.yml" in quality + ) + assert ( + "coverage_include: scripts/ci/organization_commercial_readiness_loop.py" + in quality + ) assert "organization_commercial_readiness_fixtures.py" in quality - assert "github.event.pull_request.head.sha" in quality + assert "--branch" in quality_gate and "--fail-under=100" in quality_gate + assert "--import-mode=importlib" in quality_gate + assert "github.event.pull_request.head.sha" in quality_gate assert "disabled workflow does not hold a lease" in doctoring assert "manual-only, explicitly marked" in doctoring assert "does not make every repository directly writable" in doctoring From e7b441f6f76ba2704194da91c13387d0ab28bbf8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:13:14 +0900 Subject: [PATCH 2/2] fix(ci): trigger the JS coverage gate on its own new contract test javascript-coverage-quality-ci.yml's pytest_target is the whole tests directory, so it already executes tests/test_exact_head_coverage_quality_gate_contract.py -- but that file was missing from the workflow's own path trigger, so an edit scoped only to that test could merge without the gate that runs it ever firing (Devin Review finding on PR #1683). Added the file to the JS caller's path list, not the org-loop caller's: org-loop's pytest_target is a narrower glob that never matches this filename, so adding it there would trigger a job that doesn't actually exercise the test. Pinned this with a new contract test. Co-Authored-By: Claude Sonnet 5 --- .../javascript-coverage-quality-ci.yml | 1 + ...xact_head_coverage_quality_gate_contract.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/.github/workflows/javascript-coverage-quality-ci.yml b/.github/workflows/javascript-coverage-quality-ci.yml index 44b3c8d335..62f3ca9261 100644 --- a/.github/workflows/javascript-coverage-quality-ci.yml +++ b/.github/workflows/javascript-coverage-quality-ci.yml @@ -9,6 +9,7 @@ on: - 'scripts/ci/javascript_coverage_gate.py' - 'tests/test_javascript_coverage_gate.py' - 'tests/test_javascript_coverage_storybook_boundary.py' + - 'tests/test_exact_head_coverage_quality_gate_contract.py' permissions: contents: read diff --git a/tests/test_exact_head_coverage_quality_gate_contract.py b/tests/test_exact_head_coverage_quality_gate_contract.py index 47330f9ec2..a5d0c12706 100644 --- a/tests/test_exact_head_coverage_quality_gate_contract.py +++ b/tests/test_exact_head_coverage_quality_gate_contract.py @@ -13,6 +13,9 @@ ) +SELF_TEST_PATH = "tests/test_exact_head_coverage_quality_gate_contract.py" + + def _text(path: Path) -> str: assert path.is_file(), f"missing workflow file: {path}" return path.read_text(encoding="utf-8") @@ -118,3 +121,18 @@ def test_javascript_and_organization_loop_callers_wire_distinct_subsystem_inputs in org_caller ) assert "timeout_minutes: 10" in org_caller + + +def test_js_caller_trigger_covers_this_contract_test_file() -> None: + """An edit to only this file must still trigger the job that runs it. + + The JS caller's pytest_target is the whole `tests` directory, so it + actually exercises this file when it runs -- unlike the org-loop caller, + whose pytest_target is a narrower glob that never matches this filename. + Without this file in the JS caller's path trigger, a change scoped only + to this test could merge without the gate that runs it ever firing. + """ + js_caller = _text(JS_CALLER) + trigger = js_caller.split("\npermissions:\n", 1)[0] + + assert f"'{SELF_TEST_PATH}'" in trigger