From 44bc4d1dc099254e0b12c664e1fa8417788f218a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 05:06:14 +0900 Subject: [PATCH 1/7] test(ci): define bounded Python support contract --- tests/test_python_support_contract.py | 56 +++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/test_python_support_contract.py diff --git a/tests/test_python_support_contract.py b/tests/test_python_support_contract.py new file mode 100644 index 000000000..e0a65335d --- /dev/null +++ b/tests/test_python_support_contract.py @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Contract tests for advertised and permanently tested CPython support.""" + +from __future__ import annotations + +import re +import tomllib +from pathlib import Path + +_ROOT = Path(__file__).resolve().parents[1] +_PYPROJECT = _ROOT / "pyproject.toml" +_CI_WORKFLOW = _ROOT / ".github" / "workflows" / "ci.yml" +_BOUNDED_REQUIRES_PYTHON = re.compile( + r">=(?P\d+)\.(?P\d+),<(?P\d+)\.(?P\d+)\Z" +) +_MATRIX_RE = re.compile(r'python-version:\s*\[(?P[^\]]+)\]') +_VERSION_RE = re.compile(r'"(?P\d+)\.(?P\d+)"') + + +def _advertised_minor_versions() -> tuple[str, ...]: + """Derive every CPython minor advertised by the bounded package metadata.""" + project = tomllib.loads(_PYPROJECT.read_text(encoding="utf-8"))["project"] + requires_python = project["requires-python"] + assert isinstance(requires_python, str) + matched = _BOUNDED_REQUIRES_PYTHON.fullmatch(requires_python) + assert matched is not None, "Requires-Python must have explicit lower and upper minor bounds" + lower_major = int(matched.group("lower_major")) + lower_minor = int(matched.group("lower_minor")) + upper_major = int(matched.group("upper_major")) + upper_minor = int(matched.group("upper_minor")) + assert lower_major == upper_major == 3 + assert upper_minor > lower_minor + return tuple(f"3.{minor}" for minor in range(lower_minor, upper_minor)) + + +def _permanent_ci_minor_versions() -> tuple[str, ...]: + """Return the explicit CPython unit-test matrix from the permanent CI workflow.""" + workflow = _CI_WORKFLOW.read_text(encoding="utf-8") + matched = _MATRIX_RE.search(workflow) + assert matched is not None, "CI must declare an explicit Python minor matrix" + return tuple( + f"{version.group('major')}.{version.group('minor')}" + for version in _VERSION_RE.finditer(matched.group("versions")) + ) + + +def test_requires_python_matches_every_permanent_ci_minor() -> None: + """Every installer-advertised CPython minor must have a permanent unit-test lane.""" + assert _advertised_minor_versions() == ( + "3.10", + "3.11", + "3.12", + "3.13", + "3.14", + ) + assert _permanent_ci_minor_versions() == _advertised_minor_versions() From 32465361a011b165aa4a9bcb433e552d224b2c61 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 05:07:12 +0900 Subject: [PATCH 2/7] test(ci): bind supported minors to quality-gate ceiling --- tests/test_python_support_contract.py | 51 +++++++++++++++------------ 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/tests/test_python_support_contract.py b/tests/test_python_support_contract.py index e0a65335d..aa26bbc90 100644 --- a/tests/test_python_support_contract.py +++ b/tests/test_python_support_contract.py @@ -10,27 +10,22 @@ _ROOT = Path(__file__).resolve().parents[1] _PYPROJECT = _ROOT / "pyproject.toml" _CI_WORKFLOW = _ROOT / ".github" / "workflows" / "ci.yml" -_BOUNDED_REQUIRES_PYTHON = re.compile( - r">=(?P\d+)\.(?P\d+),<(?P\d+)\.(?P\d+)\Z" +_REQUIRES_PYTHON_RE = re.compile( + r">=(?P\d+)\.(?P\d+)\Z" ) _MATRIX_RE = re.compile(r'python-version:\s*\[(?P[^\]]+)\]') _VERSION_RE = re.compile(r'"(?P\d+)\.(?P\d+)"') +_QUALITY_VERSION_RE = re.compile(r'python-version:\s*"(?P\d+)\.(?P\d+)"') -def _advertised_minor_versions() -> tuple[str, ...]: - """Derive every CPython minor advertised by the bounded package metadata.""" +def _advertised_lower_bound() -> tuple[int, int]: + """Return the exact CPython lower bound advertised by project metadata.""" project = tomllib.loads(_PYPROJECT.read_text(encoding="utf-8"))["project"] requires_python = project["requires-python"] assert isinstance(requires_python, str) - matched = _BOUNDED_REQUIRES_PYTHON.fullmatch(requires_python) - assert matched is not None, "Requires-Python must have explicit lower and upper minor bounds" - lower_major = int(matched.group("lower_major")) - lower_minor = int(matched.group("lower_minor")) - upper_major = int(matched.group("upper_major")) - upper_minor = int(matched.group("upper_minor")) - assert lower_major == upper_major == 3 - assert upper_minor > lower_minor - return tuple(f"3.{minor}" for minor in range(lower_minor, upper_minor)) + matched = _REQUIRES_PYTHON_RE.fullmatch(requires_python) + assert matched is not None, "Requires-Python must expose one explicit CPython lower bound" + return int(matched.group("major")), int(matched.group("minor")) def _permanent_ci_minor_versions() -> tuple[str, ...]: @@ -44,13 +39,25 @@ def _permanent_ci_minor_versions() -> tuple[str, ...]: ) -def test_requires_python_matches_every_permanent_ci_minor() -> None: - """Every installer-advertised CPython minor must have a permanent unit-test lane.""" - assert _advertised_minor_versions() == ( - "3.10", - "3.11", - "3.12", - "3.13", - "3.14", +def _quality_gate_python_version() -> tuple[int, int]: + """Return the CPython minor that runs coverage, docstring, and package gates.""" + workflow = _CI_WORKFLOW.read_text(encoding="utf-8") + quality_section = workflow.split(" quality-gates:\n", 1)[1].split( + " container-builds:\n", 1 + )[0] + matched = _QUALITY_VERSION_RE.search(quality_section) + assert matched is not None, "quality gates must pin an explicit Python minor" + return int(matched.group("major")), int(matched.group("minor")) + + +def test_requires_python_has_gapless_permanent_ci_evidence() -> None: + """Every currently governed supported minor must have a permanent unit-test lane.""" + lower_major, lower_minor = _advertised_lower_bound() + quality_major, quality_minor = _quality_gate_python_version() + assert lower_major == quality_major == 3 + assert quality_minor >= lower_minor + expected = tuple( + f"{lower_major}.{minor}" for minor in range(lower_minor, quality_minor + 1) ) - assert _permanent_ci_minor_versions() == _advertised_minor_versions() + assert expected == ("3.10", "3.11", "3.12", "3.13", "3.14") + assert _permanent_ci_minor_versions() == expected From 5e256ccd990012807853ea140589eadc09333407 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 05:07:42 +0900 Subject: [PATCH 3/7] fix(ci): test every currently supported Python minor --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe5614cd7..f72b29ff9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.12", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Harden runner uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 From 911e975692e4c4a41a92138d22605a601e55bf0f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 05:14:47 +0900 Subject: [PATCH 4/7] test(ci): align legacy workflow contract with support matrix --- tests/test_workflow_contracts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_workflow_contracts.py b/tests/test_workflow_contracts.py index 1fd292bab..6c0315f24 100644 --- a/tests/test_workflow_contracts.py +++ b/tests/test_workflow_contracts.py @@ -137,7 +137,7 @@ def test_ci_workflow_enforces_supported_versions_and_quality_gates() -> None: assert "pull_request:" in workflow assert "branches: [main]" in workflow - assert 'python-version: ["3.10", "3.12", "3.14"]' in workflow + assert 'python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]' in workflow assert "uv sync --locked" in workflow assert "uv run ruff check pg_llm_batch tests" in workflow assert "interrogate --fail-under 100 pg_llm_batch" in workflow From 2af2e2a73583e87c92bb05342ebe192511fbe110 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 16:06:18 -0700 Subject: [PATCH 5/7] fix(ci): keep Python support contract compatible with 3.10 --- tests/test_python_support_contract.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_python_support_contract.py b/tests/test_python_support_contract.py index aa26bbc90..85e0fc6b7 100644 --- a/tests/test_python_support_contract.py +++ b/tests/test_python_support_contract.py @@ -4,7 +4,6 @@ from __future__ import annotations import re -import tomllib from pathlib import Path _ROOT = Path(__file__).resolve().parents[1] @@ -13,6 +12,9 @@ _REQUIRES_PYTHON_RE = re.compile( r">=(?P\d+)\.(?P\d+)\Z" ) +_REQUIRES_PYTHON_SETTING_RE = re.compile( + r'^requires-python\s*=\s*"(?P[^"]+)"\s*$', re.MULTILINE +) _MATRIX_RE = re.compile(r'python-version:\s*\[(?P[^\]]+)\]') _VERSION_RE = re.compile(r'"(?P\d+)\.(?P\d+)"') _QUALITY_VERSION_RE = re.compile(r'python-version:\s*"(?P\d+)\.(?P\d+)"') @@ -20,9 +22,11 @@ def _advertised_lower_bound() -> tuple[int, int]: """Return the exact CPython lower bound advertised by project metadata.""" - project = tomllib.loads(_PYPROJECT.read_text(encoding="utf-8"))["project"] - requires_python = project["requires-python"] - assert isinstance(requires_python, str) + pyproject = _PYPROJECT.read_text(encoding="utf-8") + project_section = pyproject.split("[project]\n", 1)[1].split("\n[", 1)[0] + setting = _REQUIRES_PYTHON_SETTING_RE.search(project_section) + assert setting is not None, "project metadata must declare Requires-Python" + requires_python = setting.group("specifier") matched = _REQUIRES_PYTHON_RE.fullmatch(requires_python) assert matched is not None, "Requires-Python must expose one explicit CPython lower bound" return int(matched.group("major")), int(matched.group("minor")) From 33dc647cd087f0b030eef27ac2fed7d5cff6ba93 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 23:16:29 +0900 Subject: [PATCH 6/7] fix(ci): preserve current parent contracts in Python support restack --- .github/workflows/ci.yml | 4 +- .github/workflows/hourly-maintenance.yml | 56 ------------------------ .github/workflows/release-acceptance.yml | 4 +- tests/test_python_support_contract.py | 4 +- tests/test_workflow_contracts.py | 36 ++++++--------- 5 files changed, 18 insertions(+), 86 deletions(-) delete mode 100644 .github/workflows/hourly-maintenance.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3558ab54d..c9b5ea338 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,8 +10,8 @@ permissions: contents: read concurrency: - group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: unit-tests: diff --git a/.github/workflows/hourly-maintenance.yml b/.github/workflows/hourly-maintenance.yml deleted file mode 100644 index c141f4693..000000000 --- a/.github/workflows/hourly-maintenance.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Hourly Commercial Maintenance - -on: - schedule: - - cron: "17 * * * *" - workflow_dispatch: - -concurrency: - group: hourly-commercial-maintenance-${{ github.repository }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - review-fix: - name: Repair actionable pull-request feedback - permissions: - actions: write - contents: read - issues: write - pull-requests: read - statuses: read - uses: ContextualWisdomLab/.github/.github/workflows/pr-review-fix-scheduler.yml@5983b41ace75040c1d81818171ca7d0f3653254e # main - with: - target_repository: ContextualWisdomLab/pg-llm-batch - base_branch: main - max_prs: "100" - max_dispatches: "1" - retry_hours: "1" - canonical_ref: 5983b41ace75040c1d81818171ca7d0f3653254e - secrets: inherit - - review-merge: - name: Revalidate and merge eligible pull requests - needs: [review-fix] - if: ${{ always() }} - permissions: - actions: write - checks: read - contents: write - id-token: write - pull-requests: write - uses: ContextualWisdomLab/.github/.github/workflows/pr-review-merge-scheduler.yml@5983b41ace75040c1d81818171ca7d0f3653254e # main - with: - base_branch: main - max_prs: "100" - trigger_reviews: true - review_dispatch_limit: "1" - branch_update_limit: "1" - enable_auto_merge: true - merge_mode: direct_or_auto - update_branches: true - stale_opencode_minutes: "90" - project_flow: github-flow - secrets: inherit diff --git a/.github/workflows/release-acceptance.yml b/.github/workflows/release-acceptance.yml index c96f8ab85..935d2df49 100644 --- a/.github/workflows/release-acceptance.yml +++ b/.github/workflows/release-acceptance.yml @@ -26,8 +26,8 @@ permissions: contents: read concurrency: - group: release-acceptance-${{ github.event.pull_request.number }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: reproducible-distributions: diff --git a/tests/test_python_support_contract.py b/tests/test_python_support_contract.py index 85e0fc6b7..f4c5ff6ca 100644 --- a/tests/test_python_support_contract.py +++ b/tests/test_python_support_contract.py @@ -9,9 +9,7 @@ _ROOT = Path(__file__).resolve().parents[1] _PYPROJECT = _ROOT / "pyproject.toml" _CI_WORKFLOW = _ROOT / ".github" / "workflows" / "ci.yml" -_REQUIRES_PYTHON_RE = re.compile( - r">=(?P\d+)\.(?P\d+)\Z" -) +_REQUIRES_PYTHON_RE = re.compile(r">=(?P\d+)\.(?P\d+)\Z") _REQUIRES_PYTHON_SETTING_RE = re.compile( r'^requires-python\s*=\s*"(?P[^"]+)"\s*$', re.MULTILINE ) diff --git a/tests/test_workflow_contracts.py b/tests/test_workflow_contracts.py index 6c0315f24..f6c88867d 100644 --- a/tests/test_workflow_contracts.py +++ b/tests/test_workflow_contracts.py @@ -195,29 +195,19 @@ def test_ci_checks_out_and_verifies_the_exact_source_head_in_every_job() -> None assert checkout_count > 0 -def test_hourly_workflow_repairs_revalidates_and_merges_pull_requests() -> None: - workflow = _read(".github/workflows/hourly-maintenance.yml") - scheduler_sha = "5983b41ace75040c1d81818171ca7d0f3653254e" - - assert 'cron: "17 * * * *"' in workflow - assert "workflow_dispatch:" in workflow - assert ( - "uses: ContextualWisdomLab/.github/.github/workflows/" - "pr-review-fix-scheduler.yml@" - ) in workflow - assert "target_repository: ContextualWisdomLab/pg-llm-batch" in workflow - assert 'retry_hours: "1"' in workflow - assert f"canonical_ref: {scheduler_sha}" in workflow - assert ( - "uses: ContextualWisdomLab/.github/.github/workflows/" - "pr-review-merge-scheduler.yml@" - ) in workflow - assert "merge_mode: direct_or_auto" in workflow - assert "trigger_reviews: true" in workflow - assert "enable_auto_merge: true" in workflow - assert "update_branches: true" in workflow - assert workflow.count(f"@{scheduler_sha}") == 2 - _assert_external_actions_are_pinned(workflow) +def test_pull_request_workflows_cancel_only_superseded_same_pr_heads() -> None: + expected_group = ( + "${{ github.workflow }}-${{ github.repository }}-" + "${{ github.event.pull_request.number || github.run_id }}" + ) + for path in (".github/workflows/ci.yml", ".github/workflows/release-acceptance.yml"): + workflow = _read(path) + assert f"group: {expected_group}" in workflow + assert "cancel-in-progress: ${{ github.event_name == 'pull_request' }}" in workflow + + +def test_repository_does_not_duplicate_central_pr_maintenance() -> None: + assert not (ROOT / ".github/workflows/hourly-maintenance.yml").exists() def test_dependabot_tracks_the_new_github_actions_manifests() -> None: From f4a5df0e597341b2ba12aef1e61f68bf7f7f96fb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 23:18:00 +0900 Subject: [PATCH 7/7] fix(ci): retain exact Python support regression fixture --- tests/test_python_support_contract.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_python_support_contract.py b/tests/test_python_support_contract.py index f4c5ff6ca..85e0fc6b7 100644 --- a/tests/test_python_support_contract.py +++ b/tests/test_python_support_contract.py @@ -9,7 +9,9 @@ _ROOT = Path(__file__).resolve().parents[1] _PYPROJECT = _ROOT / "pyproject.toml" _CI_WORKFLOW = _ROOT / ".github" / "workflows" / "ci.yml" -_REQUIRES_PYTHON_RE = re.compile(r">=(?P\d+)\.(?P\d+)\Z") +_REQUIRES_PYTHON_RE = re.compile( + r">=(?P\d+)\.(?P\d+)\Z" +) _REQUIRES_PYTHON_SETTING_RE = re.compile( r'^requires-python\s*=\s*"(?P[^"]+)"\s*$', re.MULTILINE )