From 9384d6afc53ecd1d9ae26c1bd3900e6ccaa7e421 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 21:38:02 +0900 Subject: [PATCH 01/20] feat(coverage): add bounded PyO3 peer-evidence gate --- .../ci/python_native_extension_peer_gate.py | 417 ++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 scripts/ci/python_native_extension_peer_gate.py diff --git a/scripts/ci/python_native_extension_peer_gate.py b/scripts/ci/python_native_extension_peer_gate.py new file mode 100644 index 000000000..479c585cc --- /dev/null +++ b/scripts/ci/python_native_extension_peer_gate.py @@ -0,0 +1,417 @@ +#!/usr/bin/env python3 +"""Classify missing PyO3 extensions and verify exact-head native peer checks.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path, PurePosixPath +import re +import sys +from typing import Any, Sequence + +try: + import tomllib +except ModuleNotFoundError: # pragma: no cover - Python 3.10 compatibility lane. + import tomli as tomllib + + +MAX_LOG_BYTES = 2_000_000 +MAX_METADATA_BYTES = 262_144 +MAX_CHECK_BYTES = 1_000_000 +DOTTED_MODULE_RE = re.compile( + r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+\Z" +) +MISSING_MODULE_RE = re.compile( + r"ModuleNotFoundError:\s+No module named ['\"]([^'\"]+)['\"]" +) +COLLECTION_ERROR_RE = re.compile( + r"^_+\s+ERROR collecting\s+.+?\s+_+\s*$", re.MULTILINE +) +INTERRUPTED_RE = re.compile( + r"Interrupted:\s+(\d+)\s+errors?\s+during\s+collection", re.IGNORECASE +) +EXCEPTION_LINE_RE = re.compile( + r"^E\s+([A-Za-z_][A-Za-z0-9_.]*(?:Error|Exception))(?::|\s*$)", + re.MULTILINE, +) +FORBIDDEN_LOG_MARKERS = ( + " output truncated:", + "INTERNALERROR>", + "Fatal Python error", + "Segmentation fault", + "ERROR at setup", + "ERROR at teardown", + "=== FAILURES ===", +) +LOCK_FILE_NAMES = { + "Cargo.lock", + "Pipfile.lock", + "poetry.lock", + "pylock.toml", + "uv.lock", +} +PACKAGING_FILE_NAMES = { + "MANIFEST.in", + "build.rs", + "setup.cfg", + "setup.py", +} + + +def _read_bounded_regular(path: Path, maximum: int) -> bytes | None: + """Return bounded regular-file bytes, or ``None`` for unsafe input.""" + + try: + if not path.is_file() or path.is_symlink(): + return None + size = path.stat().st_size + if size > maximum: + return None + return path.read_bytes() + except OSError: + return None + + +def _read_text(path: Path, maximum: int) -> str | None: + """Return bounded UTF-8 text, rejecting malformed or unsafe input.""" + + payload = _read_bounded_regular(path, maximum) + if payload is None: + return None + try: + return payload.decode("utf-8") + except UnicodeDecodeError: + return None + + +def _safe_relative_path(raw_path: str) -> PurePosixPath | None: + """Return a normalized repository-relative POSIX path when safe.""" + + if not raw_path or "\x00" in raw_path or "\\" in raw_path: + return None + segments = raw_path.split("/") + if any(segment in {"", ".", ".."} for segment in segments): + return None + return PurePosixPath(raw_path) + + +def _maturin_contract( + pyproject: Path, +) -> tuple[str, PurePosixPath, PurePosixPath] | None: + """Return the native module, Cargo manifest, and Python source directory.""" + + payload = _read_bounded_regular(pyproject, MAX_METADATA_BYTES) + if payload is None: + return None + try: + metadata = tomllib.loads(payload.decode("utf-8")) + except (UnicodeDecodeError, tomllib.TOMLDecodeError): + return None + build_system = metadata.get("build-system") + tool = metadata.get("tool") + if not isinstance(build_system, dict) or not isinstance(tool, dict): + return None + maturin = tool.get("maturin") + if not isinstance(maturin, dict): + return None + if build_system.get("build-backend") != "maturin": + return None + if maturin.get("bindings") != "pyo3": + return None + + module_name = maturin.get("module-name") + manifest_value = maturin.get("manifest-path", "Cargo.toml") + python_source_value = maturin.get("python-source", ".") + if ( + not isinstance(module_name, str) + or DOTTED_MODULE_RE.fullmatch(module_name) is None + or not isinstance(manifest_value, str) + or not isinstance(python_source_value, str) + ): + return None + manifest_path = _safe_relative_path(manifest_value) + python_source = ( + PurePosixPath(".") + if python_source_value == "." + else _safe_relative_path(python_source_value) + ) + if ( + manifest_path is None + or manifest_path.name != "Cargo.toml" + or python_source is None + ): + return None + return module_name, manifest_path, python_source + + +def _read_changed_files(path: Path) -> tuple[PurePosixPath, ...] | None: + """Return validated changed paths from a bounded newline-delimited file.""" + + text = _read_text(path, MAX_METADATA_BYTES) + if text is None: + return None + paths: list[PurePosixPath] = [] + seen: set[str] = set() + for raw_line in text.splitlines(): + raw_path = raw_line.strip() + if not raw_path: + continue + parsed = _safe_relative_path(raw_path) + if parsed is None or parsed.as_posix() in seen: + return None + seen.add(parsed.as_posix()) + paths.append(parsed) + return tuple(paths) + + +def _touches_native_or_trust_boundary( + changed_paths: tuple[PurePosixPath, ...], + *, + pyproject_path: PurePosixPath, + manifest_path: PurePosixPath, + module_name: str, + python_source: PurePosixPath, +) -> bool: + """Return whether changed files invalidate unchanged-extension deferral.""" + + manifest_parent = manifest_path.parent + module_stub = ( + python_source / PurePosixPath(*module_name.split(".")) + ).with_suffix(".pyi") + for path in changed_paths: + path_text = path.as_posix() + if path == pyproject_path or path == manifest_path: + return True + if path.name in LOCK_FILE_NAMES or path.name in PACKAGING_FILE_NAMES: + return True + if path.name == "Cargo.toml" or path.suffix == ".rs": + return True + if path == module_stub: + return True + if path.parts[:2] in {(".github", "workflows"), (".github", "actions")}: + return True + if path.name.startswith("requirements") and path.suffix == ".txt": + return True + if manifest_parent != PurePosixPath(".") and path.is_relative_to(manifest_parent): + return True + if path_text.endswith("/pyproject.toml"): + return True + return False + + +def classify_pytest_failure( + log_text: str, + *, + module_name: str, +) -> bool: + """Return whether pytest failed only because one declared module was absent.""" + + if not log_text or any(marker in log_text for marker in FORBIDDEN_LOG_MARKERS): + return False + if re.search(r"^FAILED\s+", log_text, re.MULTILINE): + return False + + missing_modules = MISSING_MODULE_RE.findall(log_text) + collection_errors = COLLECTION_ERROR_RE.findall(log_text) + interruptions = INTERRUPTED_RE.findall(log_text) + if ( + not missing_modules + or not collection_errors + or len(interruptions) != 1 + or any(name != module_name for name in missing_modules) + ): + return False + if len(missing_modules) != len(collection_errors): + return False + if int(interruptions[0]) != len(collection_errors): + return False + + escaped_module = re.escape(module_name) + imported_module_count = len( + re.findall( + rf"^\s*(?:from\s+{escaped_module}\s+import|import\s+{escaped_module}(?:\s|$))", + log_text, + re.MULTILINE, + ) + ) + if imported_module_count < len(collection_errors): + return False + + exception_types = EXCEPTION_LINE_RE.findall(log_text) + return bool(exception_types) and all( + exception_type == "ModuleNotFoundError" + for exception_type in exception_types + ) + + +def classify_pytest_inputs( + *, + log_path: Path, + pyproject_path: Path, + changed_files_path: Path, +) -> str | None: + """Return the safely deferred module name, or ``None`` when blocking.""" + + contract = _maturin_contract(pyproject_path) + log_text = _read_text(log_path, MAX_LOG_BYTES) + changed_paths = _read_changed_files(changed_files_path) + if contract is None or log_text is None or changed_paths is None: + return None + + module_name, manifest_path, python_source = contract + project_root = pyproject_path.parent + try: + relative_pyproject = PurePosixPath( + pyproject_path.resolve().relative_to(project_root.resolve()).as_posix() + ) + except (OSError, ValueError): + return None + if _touches_native_or_trust_boundary( + changed_paths, + pyproject_path=relative_pyproject, + manifest_path=manifest_path, + module_name=module_name, + python_source=python_source, + ): + return None + if not classify_pytest_failure(log_text, module_name=module_name): + return None + return module_name + + +def _workflow_name(check: dict[str, Any]) -> str | None: + """Return a normalized workflow name from one check-run record.""" + + workflow = check.get("workflow") + if isinstance(workflow, str): + return workflow + suite = check.get("checkSuite") + if not isinstance(suite, dict): + return None + workflow_run = suite.get("workflowRun") + if not isinstance(workflow_run, dict): + return None + nested = workflow_run.get("workflow") + if not isinstance(nested, dict): + return None + name = nested.get("name") + return name if isinstance(name, str) else None + + +def _read_checks(path: Path) -> list[dict[str, Any]] | None: + """Return a bounded list of normalized check-run records.""" + + text = _read_text(path, MAX_CHECK_BYTES) + if text is None: + return None + try: + payload = json.loads(text) + except json.JSONDecodeError: + return None + if not isinstance(payload, list) or any(not isinstance(item, dict) for item in payload): + return None + return payload + + +def has_required_exact_head_checks( + checks: list[dict[str, Any]], + *, + head_sha: str, + required_checks: tuple[tuple[str, str], ...], +) -> bool: + """Return whether every trusted exact-head check completed successfully.""" + + if re.fullmatch(r"[0-9a-fA-F]{40}", head_sha) is None or not required_checks: + return False + if len(set(required_checks)) != len(required_checks): + return False + + for workflow, name in required_checks: + matches = [ + check + for check in checks + if check.get("__typename") == "CheckRun" + and _workflow_name(check) == workflow + and check.get("name") == name + and check.get("head_sha") == head_sha + ] + if not matches: + return False + if any( + str(check.get("status") or "").upper() != "COMPLETED" + or str(check.get("conclusion") or "").upper() != "SUCCESS" + for check in matches + ): + return False + return True + + +def _parse_required_check(value: str) -> tuple[str, str]: + """Parse one trusted ``WORKFLOW::CHECK`` requirement.""" + + workflow, separator, name = value.partition("::") + if not separator or not workflow.strip() or not name.strip(): + raise argparse.ArgumentTypeError( + "required checks must use non-empty WORKFLOW::CHECK syntax" + ) + return workflow.strip(), name.strip() + + +def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: + """Parse the native-extension peer-gate command line.""" + + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="command", required=True) + + classify = subparsers.add_parser("classify-pytest") + classify.add_argument("--log", type=Path, required=True) + classify.add_argument("--pyproject", type=Path, required=True) + classify.add_argument("--changed-files", type=Path, required=True) + + require = subparsers.add_parser("require-checks") + require.add_argument("--checks-json", type=Path, required=True) + require.add_argument("--head-sha", required=True) + require.add_argument( + "--required-check", + action="append", + type=_parse_required_check, + required=True, + ) + return parser.parse_args(argv) + + +def main(argv: Sequence[str] | None = None) -> int: + """Run the selected fail-closed native-extension peer gate.""" + + args = parse_args(argv) + if args.command == "classify-pytest": + module_name = classify_pytest_inputs( + log_path=args.log, + pyproject_path=args.pyproject, + changed_files_path=args.changed_files, + ) + if module_name is None: + print("pytest failure is not safely deferrable", file=sys.stderr) + return 1 + print( + "pytest collection failed exclusively because unchanged declared " + f"native module {module_name} was absent" + ) + return 0 + + checks = _read_checks(args.checks_json) + required_checks = tuple(args.required_check) + if checks is not None and has_required_exact_head_checks( + checks, + head_sha=args.head_sha, + required_checks=required_checks, + ): + print("all required exact-head native peer checks succeeded") + return 0 + print("required exact-head native peer checks were not proven", file=sys.stderr) + return 1 + + +if __name__ == "__main__": # pragma: no cover - exercised by workflow entrypoint. + raise SystemExit(main()) From 27affda8165df46da09067e182b3d9c2ada05315 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 21:41:51 +0900 Subject: [PATCH 02/20] test(coverage): prove PyO3 peer gate fail-closed --- .../test_python_native_extension_peer_gate.py | 561 ++++++++++++++++++ 1 file changed, 561 insertions(+) create mode 100644 tests/test_python_native_extension_peer_gate.py diff --git a/tests/test_python_native_extension_peer_gate.py b/tests/test_python_native_extension_peer_gate.py new file mode 100644 index 000000000..74272c48a --- /dev/null +++ b/tests/test_python_native_extension_peer_gate.py @@ -0,0 +1,561 @@ +"""Tests for the bounded PyO3 native-extension peer gate.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path, PurePosixPath + +import pytest + +from scripts.ci import python_native_extension_peer_gate as gate + + +PYPROJECT = """\ +[build-system] +requires = ["maturin>=1.10,<2.0"] +build-backend = "maturin" + +[tool.maturin] +bindings = "pyo3" +manifest-path = "crates/fast-mlsirm-py/Cargo.toml" +module-name = "fast_mlsirm._core" +python-source = "python" +""" + +LOG = """\ +============================= test session starts ============================== +collected 0 items / 2 errors + +_____________ ERROR collecting tests/test_cov_f_fit.py ______________ +ImportError while importing test module '/work/tests/test_cov_f_fit.py'. +Traceback: +/usr/lib/python3/importlib/__init__.py:126: in import_module + return _bootstrap._gcd_import(name[level:], package, level) +tests/test_cov_f_fit.py:4: in + from fast_mlsirm._core import neg_loglik_and_grad +E ModuleNotFoundError: No module named 'fast_mlsirm._core' +_____________ ERROR collecting tests/test_mle.py ______________ +ImportError while importing test module '/work/tests/test_mle.py'. +Traceback: +tests/test_mle.py:3: in + import fast_mlsirm._core +E ModuleNotFoundError: No module named "fast_mlsirm._core" +!!!!!!!!!!!!!!!!!!! Interrupted: 2 errors during collection !!!!!!!!!!!!!!!!!!!! +============================== 2 errors in 0.42s =============================== +""" + + +def write(path: Path, text: str) -> Path: + """Write UTF-8 fixture text and return its path.""" + + path.write_text(text, encoding="utf-8") + return path + + +def valid_inputs(tmp_path: Path) -> tuple[Path, Path, Path]: + """Create one valid log, pyproject, and changed-file fixture.""" + + return ( + write(tmp_path / "pytest.log", LOG), + write(tmp_path / "pyproject.toml", PYPROJECT), + write( + tmp_path / "changed.txt", + "python/fast_mlsirm/scoring/reporting.py\n" + "tests/test_scoring_reporting.py\n", + ), + ) + + +@pytest.mark.parametrize( + ("raw", "expected"), + [ + ("src/lib.rs", "src/lib.rs"), + ("", None), + ("../src/lib.rs", None), + ("/src/lib.rs", None), + ("src\\lib.rs", None), + ("src/\x00lib.rs", None), + ("./src/lib.rs", None), + ], +) +def test_safe_relative_path(raw: str, expected: str | None) -> None: + """Repository paths reject traversal, aliases, separators, and NUL.""" + + result = gate._safe_relative_path(raw) + assert (result.as_posix() if result is not None else None) == expected + + +def test_bounded_reader_rejects_missing_directory_symlink_large_and_oserror( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """Only bounded regular files are accepted.""" + + missing = tmp_path / "missing" + directory = tmp_path / "directory" + directory.mkdir() + target = write(tmp_path / "target", "ok") + symlink = tmp_path / "link" + symlink.symlink_to(target) + large = write(tmp_path / "large", "abcd") + + assert gate._read_bounded_regular(missing, 10) is None + assert gate._read_bounded_regular(directory, 10) is None + assert gate._read_bounded_regular(symlink, 10) is None + assert gate._read_bounded_regular(large, 3) is None + assert gate._read_bounded_regular(target, 10) == b"ok" + + monkeypatch.setattr(Path, "read_bytes", lambda _self: (_ for _ in ()).throw(OSError())) + assert gate._read_bounded_regular(target, 10) is None + + +def test_read_text_rejects_non_utf8(tmp_path: Path) -> None: + """Malformed UTF-8 cannot influence classification.""" + + path = tmp_path / "bad" + path.write_bytes(b"\xff") + assert gate._read_text(path, 10) is None + + +@pytest.mark.parametrize( + "replacement", + [ + 'build-backend = "setuptools.build_meta"', + 'bindings = "cffi"', + 'module-name = "not_dotted"', + 'manifest-path = "../Cargo.toml"', + 'manifest-path = "Cargo.lock"', + ], +) +def test_maturin_contract_rejects_invalid_contracts( + tmp_path: Path, replacement: str +) -> None: + """The classifier requires explicit safe maturin/PyO3 metadata.""" + + content = PYPROJECT + if replacement.startswith("build-backend"): + content = content.replace('build-backend = "maturin"', replacement) + elif replacement.startswith("bindings"): + content = content.replace('bindings = "pyo3"', replacement) + elif replacement.startswith("module-name"): + content = content.replace('module-name = "fast_mlsirm._core"', replacement) + else: + content = content.replace( + 'manifest-path = "crates/fast-mlsirm-py/Cargo.toml"', replacement + ) + assert gate._maturin_contract(write(tmp_path / "pyproject.toml", content)) is None + + +@pytest.mark.parametrize( + "content", + [ + "", + "not = [valid", + "[build-system]\nbuild-backend = \"maturin\"\n", + "[tool]\nvalue = 1\n", + "[tool.maturin]\nbindings = \"pyo3\"\nmodule-name = \"a.b\"\n", + "[build-system]\nbuild-backend = \"maturin\"\n[tool]\nmaturin = 1\n", + ( + "[build-system]\nbuild-backend = \"maturin\"\n" + "[tool.maturin]\nbindings = \"pyo3\"\nmodule-name = 3\n" + ), + ], +) +def test_maturin_contract_rejects_malformed_metadata( + tmp_path: Path, content: str +) -> None: + """Missing and malformed TOML structures fail closed.""" + + assert gate._maturin_contract(write(tmp_path / "pyproject.toml", content)) is None + + +def test_maturin_contract_uses_default_manifest(tmp_path: Path) -> None: + """A safe root Cargo manifest is the maturin default.""" + + content = PYPROJECT.replace( + 'manifest-path = "crates/fast-mlsirm-py/Cargo.toml"\n', "" + ) + assert gate._maturin_contract(write(tmp_path / "pyproject.toml", content)) == ( + "fast_mlsirm._core", + PurePosixPath("Cargo.toml"), + PurePosixPath("python"), + ) + + +@pytest.mark.parametrize( + "changed", + [ + "pyproject.toml\n", + "Cargo.toml\n", + "Cargo.lock\n", + "src/lib.rs\n", + "build.rs\n", + "setup.py\n", + "requirements-ci.txt\n", + ".github/workflows/ci.yml\n", + ".github/actions/setup/action.yml\n", + "python/fast_mlsirm/_core.pyi\n", + "crates/fast-mlsirm-py/README.md\n", + "nested/pyproject.toml\n", + "uv.lock\n", + ], +) +def test_native_trust_boundary_changes_block_deferral( + tmp_path: Path, changed: str +) -> None: + """Native, packaging, dependency, and CI changes require direct builds.""" + + log, pyproject, changed_path = valid_inputs(tmp_path) + changed_path.write_text(changed, encoding="utf-8") + assert gate.classify_pytest_inputs( + log_path=log, + pyproject_path=pyproject, + changed_files_path=changed_path, + ) is None + + +@pytest.mark.parametrize( + "changed", + [ + "../bad.py\n", + "same.py\nsame.py\n", + "C:\\bad.py\n", + ], +) +def test_changed_file_list_rejects_unsafe_entries( + tmp_path: Path, changed: str +) -> None: + """Untrusted path lists reject traversal, duplicates, and platform aliases.""" + + path = write(tmp_path / "changed.txt", changed) + assert gate._read_changed_files(path) is None + + +def test_changed_file_list_ignores_blank_lines(tmp_path: Path) -> None: + """Blank lines do not create path aliases.""" + + path = write(tmp_path / "changed.txt", "\npython/pkg.py\n\n") + assert gate._read_changed_files(path) == (Path("python/pkg.py"),) + + +@pytest.mark.parametrize( + "log", + [ + "", + LOG.replace("fast_mlsirm._core", "other_module", 1), + LOG.replace("Interrupted: 2 errors", "Interrupted: 1 error"), + LOG.replace("Interrupted: 2 errors", "Interrupted: 2 errors") + "\nFAILED x.py::test_x\n", + LOG + "\n=== FAILURES ===\n", + LOG + "\nINTERNALERROR> boom\n", + LOG + "\nFatal Python error\n", + LOG + "\nSegmentation fault\n", + LOG + "\nERROR at setup\n", + LOG + "\nERROR at teardown\n", + LOG.replace( + "E ModuleNotFoundError: No module named 'fast_mlsirm._core'", + "E ImportError: bad import", + 1, + ), + LOG.replace( + "_____________ ERROR collecting tests/test_mle.py ______________\n", "" + ), + LOG.replace( + 'E ModuleNotFoundError: No module named "fast_mlsirm._core"\n', "" + ), + LOG.replace("Interrupted: 2 errors during collection", "no interruption"), + LOG + "\n output truncated: 999 lines\n", + ], +) +def test_pytest_classifier_rejects_ambiguous_or_mixed_failures(log: str) -> None: + """Only complete, exclusive declared-module collection failures defer.""" + + assert not gate.classify_pytest_failure( + log, + module_name="fast_mlsirm._core", + ) + + +def test_pytest_classifier_accepts_exact_missing_extension() -> None: + """A complete exact-module collection failure is classifiable.""" + + assert gate.classify_pytest_failure( + LOG, + module_name="fast_mlsirm._core", + ) + + +def test_classify_inputs_accepts_python_only_change(tmp_path: Path) -> None: + """Python-only changes may defer to trusted native peer evidence.""" + + log, pyproject, changed = valid_inputs(tmp_path) + assert gate.classify_pytest_inputs( + log_path=log, + pyproject_path=pyproject, + changed_files_path=changed, + ) == "fast_mlsirm._core" + + +def test_classify_inputs_rejects_unsafe_input_files(tmp_path: Path) -> None: + """Missing or malformed inputs block classification.""" + + log, pyproject, changed = valid_inputs(tmp_path) + log.unlink() + assert gate.classify_pytest_inputs( + log_path=log, + pyproject_path=pyproject, + changed_files_path=changed, + ) is None + + +def test_workflow_name_supports_flat_and_nested_records() -> None: + """Check records normalize trusted flat and GraphQL workflow names.""" + + assert gate._workflow_name({"workflow": "CI"}) == "CI" + assert gate._workflow_name({}) is None + assert gate._workflow_name({"checkSuite": 1}) is None + assert gate._workflow_name({"checkSuite": {"workflowRun": 1}}) is None + assert gate._workflow_name( + {"checkSuite": {"workflowRun": {"workflow": 1}}} + ) is None + assert gate._workflow_name( + {"checkSuite": {"workflowRun": {"workflow": {"name": 1}}}} + ) is None + assert gate._workflow_name( + {"checkSuite": {"workflowRun": {"workflow": {"name": "CI"}}}} + ) == "CI" + + +def successful_checks(head: str) -> list[dict[str, object]]: + """Return exact-head Python, Rust, and package check runs.""" + + return [ + { + "__typename": "CheckRun", + "workflow": "CI", + "name": name, + "head_sha": head, + "status": "COMPLETED", + "conclusion": "SUCCESS", + } + for name in ("python", "rust", "package") + ] + + +@pytest.mark.parametrize( + "mutation", + [ + lambda checks: checks.pop(), + lambda checks: checks[0].update(head_sha="b" * 40), + lambda checks: checks[0].update(status="IN_PROGRESS"), + lambda checks: checks[0].update(conclusion="FAILURE"), + lambda checks: checks[0].update(__typename="StatusContext"), + lambda checks: checks[0].update(workflow="Other"), + lambda checks: checks[0].update(name="Python"), + ], +) +def test_exact_head_checks_reject_missing_stale_pending_or_spoofed( + mutation, +) -> None: + """Every required exact-head CheckRun must complete successfully.""" + + head = "a" * 40 + checks = successful_checks(head) + mutation(checks) + assert not gate.has_required_exact_head_checks( + checks, + head_sha=head, + required_checks=( + ("CI", "python"), + ("CI", "rust"), + ("CI", "package"), + ), + ) + + +def test_exact_head_checks_accept_nested_workflow_records() -> None: + """GraphQL-shaped workflow names remain acceptable after normalization.""" + + head = "a" * 40 + checks = successful_checks(head) + checks[0].pop("workflow") + checks[0]["checkSuite"] = { + "workflowRun": {"workflow": {"name": "CI"}} + } + assert gate.has_required_exact_head_checks( + checks, + head_sha=head, + required_checks=( + ("CI", "python"), + ("CI", "rust"), + ("CI", "package"), + ), + ) + + +@pytest.mark.parametrize( + ("head", "required"), + [ + ("bad", (("CI", "python"),)), + ("a" * 40, ()), + ("a" * 40, (("CI", "python"), ("CI", "python"))), + ], +) +def test_exact_head_checks_reject_invalid_contract( + head: str, required: tuple[tuple[str, str], ...] +) -> None: + """Malformed SHAs and duplicate or empty requirements fail closed.""" + + assert not gate.has_required_exact_head_checks( + successful_checks("a" * 40), + head_sha=head, + required_checks=required, + ) + + +def test_read_checks_rejects_unsafe_or_invalid_json(tmp_path: Path) -> None: + """Peer evidence must be a bounded JSON list of objects.""" + + assert gate._read_checks(write(tmp_path / "bad.json", "{")) is None + assert gate._read_checks(write(tmp_path / "scalar.json", "{}")) is None + assert gate._read_checks(write(tmp_path / "mixed.json", "[1]")) is None + valid = write(tmp_path / "valid.json", '[{"name":"python"}]') + assert gate._read_checks(valid) == [{"name": "python"}] + + +@pytest.mark.parametrize( + "value", + ["CI::python", " CI :: python "], +) +def test_parse_required_check(value: str) -> None: + """Trusted check specifications use exact workflow and job names.""" + + assert gate._parse_required_check(value) == ("CI", "python") + + +@pytest.mark.parametrize("value", ["CI", "::python", "CI::"]) +def test_parse_required_check_rejects_malformed(value: str) -> None: + """Empty or delimiter-free check specifications are rejected.""" + + with pytest.raises(argparse.ArgumentTypeError): + gate._parse_required_check(value) + + +def test_maturin_contract_rejects_unsafe_file(tmp_path: Path) -> None: + """Missing project metadata cannot define a native peer contract.""" + + assert gate._maturin_contract(tmp_path / "missing.toml") is None + + +def test_changed_file_reader_rejects_missing_file(tmp_path: Path) -> None: + """Missing changed-file evidence blocks deferral.""" + + assert gate._read_changed_files(tmp_path / "missing.txt") is None + + +def test_classify_inputs_rejects_resolve_failure( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """Filesystem resolution failures do not produce a deferral.""" + + log, pyproject, changed = valid_inputs(tmp_path) + original = Path.resolve + + def fail_pyproject(path: Path, *args, **kwargs): + """Raise only while the classifier resolves the project file.""" + + if path == pyproject: + raise OSError("unavailable") + return original(path, *args, **kwargs) + + monkeypatch.setattr(Path, "resolve", fail_pyproject) + assert gate.classify_pytest_inputs( + log_path=log, + pyproject_path=pyproject, + changed_files_path=changed, + ) is None + + +def test_classify_inputs_rejects_nonmatching_log(tmp_path: Path) -> None: + """Valid metadata cannot defer an unrelated pytest failure.""" + + log, pyproject, changed = valid_inputs(tmp_path) + log.write_text("FAILED tests/test_x.py::test_x\n", encoding="utf-8") + assert gate.classify_pytest_inputs( + log_path=log, + pyproject_path=pyproject, + changed_files_path=changed, + ) is None + + +def test_read_checks_rejects_missing_file(tmp_path: Path) -> None: + """Missing peer-check evidence blocks approval.""" + + assert gate._read_checks(tmp_path / "missing.json") is None + + +def test_cli_classify_and_require_checks(tmp_path: Path, capsys) -> None: + """Both CLI operations emit explicit success and fail-closed diagnostics.""" + + log, pyproject, changed = valid_inputs(tmp_path) + assert gate.main( + [ + "classify-pytest", + "--log", + str(log), + "--pyproject", + str(pyproject), + "--changed-files", + str(changed), + ] + ) == 0 + assert "unchanged declared native module" in capsys.readouterr().out + + changed.write_text("Cargo.toml\n", encoding="utf-8") + assert gate.main( + [ + "classify-pytest", + "--log", + str(log), + "--pyproject", + str(pyproject), + "--changed-files", + str(changed), + ] + ) == 1 + assert "not safely deferrable" in capsys.readouterr().err + + head = "a" * 40 + checks_path = write( + tmp_path / "checks.json", + json.dumps(successful_checks(head)), + ) + assert gate.main( + [ + "require-checks", + "--checks-json", + str(checks_path), + "--head-sha", + head, + "--required-check", + "CI::python", + "--required-check", + "CI::rust", + "--required-check", + "CI::package", + ] + ) == 0 + assert "all required exact-head" in capsys.readouterr().out + + checks_path.write_text("[]", encoding="utf-8") + assert gate.main( + [ + "require-checks", + "--checks-json", + str(checks_path), + "--head-sha", + head, + "--required-check", + "CI::python", + ] + ) == 1 + assert "not proven" in capsys.readouterr().err From da6c909c93f4b5698695333266b67121b7626e25 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 21:43:31 +0900 Subject: [PATCH 03/20] docs(coverage): record PyO3 peer-evidence boundary --- .../python-native-extension-peer-evidence.md | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 docs/doctoring/python-native-extension-peer-evidence.md diff --git a/docs/doctoring/python-native-extension-peer-evidence.md b/docs/doctoring/python-native-extension-peer-evidence.md new file mode 100644 index 000000000..ae0ab59c7 --- /dev/null +++ b/docs/doctoring/python-native-extension-peer-evidence.md @@ -0,0 +1,175 @@ +# Doctoring record: Python native-extension peer evidence + +## Purpose + +The central OpenCode coverage sandbox executes pull-request tests without a +repository credential, package-index access, or permission to run +pull-request-selected build/install hooks. That isolation is intentional, but a +mixed Rust/Python project can require a compiled PyO3 extension during pytest +collection. A plain source checkout then raises `ModuleNotFoundError` before any +Python test is collected even when the exact pull-request head has already built, +installed, and tested the extension in trusted repository jobs. + +This record defines a bounded classifier and exact-head peer-evidence gate. The +classifier does **not** convert a missing extension into passing test evidence. +It can only identify one narrow execution-environment limitation and defer the +final decision to separately successful native build and test checks on the same +commit. + +## Observed failure + +`ContextualWisdomLab/fast-mlsirm#546` uses the maturin mixed-project layout: + +```toml +[build-system] +build-backend = "maturin" + +[tool.maturin] +bindings = "pyo3" +manifest-path = "crates/fast-mlsirm-py/Cargo.toml" +module-name = "fast_mlsirm._core" +python-source = "python" +``` + +The repository CI first builds and installs the native module and then runs +pytest. The isolated central source sandbox deliberately does not perform that +build, so collection stops at: + +```text +ModuleNotFoundError: No module named 'fast_mlsirm._core' +``` + +Maturin documents that `module-name` places the compiled extension inside the +configured Python source tree and that `maturin develop` or an installation step +materializes the shared library. PyO3 likewise documents that a native module +must be compiled and exposed with the matching module name before Python can +import it. The source checkout alone is therefore not equivalent to the +installed package. + +## Classification contract + +`scripts/ci/python_native_extension_peer_gate.py classify-pytest` accepts a +failure only when every condition below is true: + +1. `pyproject.toml` is a bounded, regular, non-symlink UTF-8 file. +2. The build backend is exactly `maturin` and bindings are exactly `pyo3`. +3. `module-name`, `manifest-path`, and `python-source` are safe relative values. +4. The pytest log is bounded, complete, and contains only collection errors. +5. Every terminal exception is `ModuleNotFoundError` for the declared module. +6. Every collection-error block contains a direct import of that module. +7. The interruption count, collection-block count, and missing-module count + agree exactly. +8. There is no failure, setup/teardown error, internal pytest error, crash, + segmentation fault, or truncation marker. +9. The changed-file list is bounded, unique, and traversal-free. +10. The pull request does not change Rust source, Cargo metadata, native stubs, + maturin metadata, dependency locks, requirements, packaging files, GitHub + workflows/actions, or any file under the native crate directory. + +A rejected classification remains an ordinary blocking test failure. + +## Exact-head peer evidence + +A successful classification is not approval. Before the central workflow may +accept it, `require-checks` must receive normalized `CheckRun` records for the +exact 40-character pull-request head and prove all trusted requirements. The +initial `fast-mlsirm` contract requires: + +```text +CI::python +CI::rust +CI::package +``` + +Every matching check must be a GitHub `CheckRun`, belong to the trusted workflow, +carry the exact head SHA, have status `COMPLETED`, and conclusion `SUCCESS`. +Missing, pending, failed, cancelled, neutral, skipped-required, stale-head, +status-only, or lookalike check records fail closed. The workflow and check names +must be supplied by trusted central or protected-base configuration, not by pull +request prose. + +GPU and fuzz evidence remain independent repository gates. The peer gate neither +removes nor reinterprets them. + +## Change-sensitive boundary + +The deferral exists only for an unchanged native/package trust boundary. Any +change to the extension implementation, Cargo manifests or lock, maturin +configuration, native stub, packaging metadata, dependency locks, or CI workflow +requires a direct trusted native build path. This prevents a pull request from +changing the thing being imported while asking the central sandbox to trust an +older binary or a weakly named passing check. + +Python business or reporting code and its tests may use the deferral when the +native boundary is unchanged, but the current-head repository Python job must +still execute the complete suite against the built extension. + +## Security and privacy boundary + +The helper reads only bounded regular files and performs no network access, +subprocess execution, package installation, token access, or mutation. It does +not load the target project as Python code. TOML and JSON are parsed as data. +Repository paths reject absolute paths, parent traversal, current-directory +aliases, Windows separators, NUL, and duplicates. + +The classifier does not make arbitrary `ModuleNotFoundError` safe. Missing +third-party dependencies, syntax/import defects in Python modules, mixed +exceptions, runtime crashes, and ordinary test failures remain blocking. + +## Testing evidence + +The focused suite includes the exact `fast_mlsirm._core` collection shape plus +adversarial cases for: + +- wrong and mixed missing modules; +- inconsistent collection counts; +- failed tests and setup/teardown errors; +- internal pytest errors, crashes, and truncated output; +- malformed TOML and unsafe paths; +- changed Rust, Cargo, packaging, dependency, workflow, and native-stub inputs; +- stale, pending, failed, status-only, wrong-workflow, and misleading checks; +- malformed SHAs, duplicate requirements, unsafe JSON, and missing files; +- flat and GraphQL-shaped workflow metadata; +- both CLI success and fail-closed paths. + +Local verification before publication reported 81 tests passing with 220/220 +production statements and 98/98 production branches covered. Permanent central +quality and security workflows remain authoritative after the branch is pushed. + +## Interpretation limits + +This gate establishes neither product correctness nor scientific validity. It +only prevents a known source-only sandbox limitation from being confused with a +Python defect while preserving exact-head native evidence. Parameter recovery, +CPU/GPU parity, psychometric validity, fairness, and release readiness remain +separate product obligations. + +## Rollback + +Rollback removes the helper, tests, and workflow integration. The prior behavior +is fail-closed: any missing native module causes central coverage failure. No +rollback requires weakening branch protection, deleting repository tests, or +introducing a Python substitute for Rust arithmetic. + +## References + +GitHub. (2026). *REST API endpoints for workflow runs*. GitHub Docs. +https://docs.github.com/en/rest/actions/workflow-runs + +Maturin contributors. (2026). *Bindings*. Maturin user guide. +https://www.maturin.rs/bindings + +Maturin contributors. (2026). *Configuration*. Maturin user guide. +https://www.maturin.rs/config + +Maturin contributors. (2026). *Introduction: Mixed Rust/Python projects*. +Maturin user guide. https://www.maturin.rs/ + +Python Software Foundation. (2026). *The import system*. Python documentation. +https://docs.python.org/3/reference/import.html + +PyO3 Project and Contributors. (2026). *Building and distribution*. PyO3 user +guide. https://pyo3.rs/main/building-and-distribution + +PyO3 Project and Contributors. (2026). *Python modules*. PyO3 user guide. +https://pyo3.rs/main/module From 68e8b1695280d11c58b5553457442527f9d0542c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 21:44:04 +0900 Subject: [PATCH 04/20] docs(changelog): record PyO3 peer-evidence gate --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e601de81b..a0e7ec61e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Semantic Versioning where the repository publishes a release. ### Added - Added exact-base `uv.lock` materialization that reconstructs standalone nested projects with a checksum-pinned official `uv` exporter, isolated frozen/offline execution, strict exact-pin and SHA-256 output validation, and complete Python 3.10/3.14 quality evidence. +- Added a bounded PyO3/maturin pytest-failure classifier and exact-head native peer-check verifier so source-only OpenCode sandboxes can distinguish one unchanged-extension collection limitation from product failures without skipping tests, executing pull-request build hooks, or weakening Rust ownership. ### Fixed From 6c4be5d226209fd76050562d2e43f03aaf29838f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:50:57 +0900 Subject: [PATCH 05/20] test(coverage): require repo-root-aware nested PyO3 trust boundaries --- ...tive_extension_peer_gate_nested_project.py | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 tests/test_python_native_extension_peer_gate_nested_project.py diff --git a/tests/test_python_native_extension_peer_gate_nested_project.py b/tests/test_python_native_extension_peer_gate_nested_project.py new file mode 100644 index 000000000..6f2080100 --- /dev/null +++ b/tests/test_python_native_extension_peer_gate_nested_project.py @@ -0,0 +1,121 @@ +"""Nested-project regressions for the PyO3 native peer-evidence classifier.""" + +from __future__ import annotations + +from pathlib import Path + +from scripts.ci import python_native_extension_peer_gate as gate + + +_PYPROJECT = """\ +[build-system] +requires = ["maturin>=1.10,<2.0"] +build-backend = "maturin" + +[tool.maturin] +bindings = "pyo3" +manifest-path = "crates/native_bridge/Cargo.toml" +module-name = "nested_package._core" +python-source = "python" +""" + +_PYTEST_LOG = """\ +============================= test session starts ============================== +collected 0 items / 1 error + +_____________ ERROR collecting tests/test_public_api.py ______________ +ImportError while importing test module '/work/services/nested/tests/test_public_api.py'. +Traceback: +tests/test_public_api.py:3: in + import nested_package._core +E ModuleNotFoundError: No module named 'nested_package._core' +!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!! +============================== 1 error in 0.20s =============================== +""" + + +def _write(path: Path, text: str) -> Path: + """Write one UTF-8 fixture and return its path.""" + + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + return path + + +def _nested_inputs(tmp_path: Path, changed: str) -> tuple[Path, Path, Path, Path]: + """Create a repository-rooted nested maturin project fixture.""" + + repository_root = tmp_path / "repository_root" + project_root = repository_root / "services" / "nested_project" + return ( + repository_root, + _write(project_root / "pytest.log", _PYTEST_LOG), + _write(project_root / "pyproject.toml", _PYPROJECT), + _write(repository_root / "changed-files.txt", changed), + ) + + +def test_nested_project_python_only_change_is_classifiable(tmp_path: Path) -> None: + """Repository-relative Python changes retain the nested project prefix.""" + + repository_root, log_path, pyproject_path, changed_files_path = _nested_inputs( + tmp_path, + "services/nested_project/python/nested_package/reporting.py\n" + "services/nested_project/tests/test_reporting.py\n", + ) + + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=repository_root, + ) == "nested_package._core" + + +def test_nested_project_native_and_metadata_changes_block_deferral( + tmp_path: Path, +) -> None: + """Nested native paths and their exact pyproject remain blocking.""" + + for changed in ( + "services/nested_project/crates/native_bridge/README.md\n", + "services/nested_project/crates/native_bridge/src/lib.rs\n", + "services/nested_project/pyproject.toml\n", + "services/nested_project/python/nested_package/_core.pyi\n", + ): + repository_root, log_path, pyproject_path, changed_files_path = ( + _nested_inputs(tmp_path / changed.replace("/", "_"), changed) + ) + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=repository_root, + ) is None + + +def test_repo_root_must_contain_the_pyproject(tmp_path: Path) -> None: + """A mismatched or unsafe repository root cannot classify a failure.""" + + repository_root, log_path, pyproject_path, changed_files_path = _nested_inputs( + tmp_path, + "services/nested_project/python/nested_package/reporting.py\n", + ) + outside_root = tmp_path / "outside_root" + outside_root.mkdir() + + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=outside_root, + ) is None + + root_link = tmp_path / "repository_link" + root_link.symlink_to(repository_root, target_is_directory=True) + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=root_link, + ) is None From 8a1570e3a3d54195fa79e6e7a652e69958468563 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:53:45 +0900 Subject: [PATCH 06/20] fix(coverage): bind nested PyO3 trust paths to repository root --- .../ci/python_native_extension_peer_gate.py | 60 ++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/scripts/ci/python_native_extension_peer_gate.py b/scripts/ci/python_native_extension_peer_gate.py index 479c585cc..a4b12c9fe 100644 --- a/scripts/ci/python_native_extension_peer_gate.py +++ b/scripts/ci/python_native_extension_peer_gate.py @@ -165,6 +165,45 @@ def _read_changed_files(path: Path) -> tuple[PurePosixPath, ...] | None: return tuple(paths) +def _repository_contract_paths( + *, + repo_root_path: Path | None, + pyproject_path: Path, + manifest_path: PurePosixPath, + python_source: PurePosixPath, +) -> tuple[PurePosixPath, PurePosixPath, PurePosixPath] | None: + """Return repository-relative PyO3 contract paths for one project.""" + + candidate_root = pyproject_path.parent if repo_root_path is None else repo_root_path + try: + if not candidate_root.is_dir() or candidate_root.is_symlink(): + return None + repository_root = candidate_root.resolve() + project_root = pyproject_path.parent.resolve() + resolved_pyproject = pyproject_path.resolve() + if resolved_pyproject.parent != project_root: + return None + project_prefix_path = project_root.relative_to(repository_root) + except (OSError, ValueError): + return None + + project_prefix = ( + PurePosixPath(".") + if not project_prefix_path.parts + else PurePosixPath(project_prefix_path.as_posix()) + ) + relative_pyproject = project_prefix / pyproject_path.name + if relative_pyproject.name != "pyproject.toml": + return None + relative_manifest = project_prefix / manifest_path + relative_python_source = ( + project_prefix + if python_source == PurePosixPath(".") + else project_prefix / python_source + ) + return relative_pyproject, relative_manifest, relative_python_source + + def _touches_native_or_trust_boundary( changed_paths: tuple[PurePosixPath, ...], *, @@ -250,6 +289,7 @@ def classify_pytest_inputs( log_path: Path, pyproject_path: Path, changed_files_path: Path, + repo_root_path: Path | None = None, ) -> str | None: """Return the safely deferred module name, or ``None`` when blocking.""" @@ -260,19 +300,21 @@ def classify_pytest_inputs( return None module_name, manifest_path, python_source = contract - project_root = pyproject_path.parent - try: - relative_pyproject = PurePosixPath( - pyproject_path.resolve().relative_to(project_root.resolve()).as_posix() - ) - except (OSError, ValueError): + repository_paths = _repository_contract_paths( + repo_root_path=repo_root_path, + pyproject_path=pyproject_path, + manifest_path=manifest_path, + python_source=python_source, + ) + if repository_paths is None: return None + relative_pyproject, relative_manifest, relative_python_source = repository_paths if _touches_native_or_trust_boundary( changed_paths, pyproject_path=relative_pyproject, - manifest_path=manifest_path, + manifest_path=relative_manifest, module_name=module_name, - python_source=python_source, + python_source=relative_python_source, ): return None if not classify_pytest_failure(log_text, module_name=module_name): @@ -368,6 +410,7 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: classify.add_argument("--log", type=Path, required=True) classify.add_argument("--pyproject", type=Path, required=True) classify.add_argument("--changed-files", type=Path, required=True) + classify.add_argument("--repo-root", type=Path) require = subparsers.add_parser("require-checks") require.add_argument("--checks-json", type=Path, required=True) @@ -390,6 +433,7 @@ def main(argv: Sequence[str] | None = None) -> int: log_path=args.log, pyproject_path=args.pyproject, changed_files_path=args.changed_files, + repo_root_path=args.repo_root, ) if module_name is None: print("pytest failure is not safely deferrable", file=sys.stderr) From 55faf74b9051fb7c8e9ad5480385140608c13aa4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:55:22 +0900 Subject: [PATCH 07/20] test(coverage): require PyO3 deferral integration and exact-head peer gate --- ...e_extension_peer_gate_workflow_contract.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/test_python_native_extension_peer_gate_workflow_contract.py diff --git a/tests/test_python_native_extension_peer_gate_workflow_contract.py b/tests/test_python_native_extension_peer_gate_workflow_contract.py new file mode 100644 index 000000000..97c8bc304 --- /dev/null +++ b/tests/test_python_native_extension_peer_gate_workflow_contract.py @@ -0,0 +1,103 @@ +"""Permanent workflow contracts for the PyO3 source-only coverage boundary.""" + +from __future__ import annotations + +from pathlib import Path + + +_ROOT = Path(__file__).parents[1] +_REVIEW_WORKFLOW = _ROOT / ".github" / "workflows" / "opencode-review-dispatch.yml" +_QUALITY_WORKFLOW = ( + _ROOT + / ".github" + / "workflows" + / "python-native-extension-peer-gate-quality-ci.yml" +) +_HELPER = "scripts/ci/python_native_extension_peer_gate.py" + + +def _review_workflow() -> str: + """Return the protected OpenCode review workflow text.""" + + return _REVIEW_WORKFLOW.read_text(encoding="utf-8") + + +def _quality_workflow() -> str: + """Return the permanent PyO3 peer-gate quality workflow text.""" + + return _QUALITY_WORKFLOW.read_text(encoding="utf-8") + + +def test_failed_python_suite_uses_bounded_repo_root_aware_classifier() -> None: + """Only a real Python failure may enter the exact PyO3 classifier.""" + + workflow = _review_workflow() + assert "python_native_peer_check_required=0" in workflow + assert "classify-pytest" in workflow + assert _HELPER in workflow + assert '--repo-root "$COVERAGE_SOURCE_WORKDIR"' in workflow + assert '--pyproject "$project_dir/pyproject.toml"' in workflow + assert "changed_files_for_coverage" in workflow + assert "python_native_pytest_log" in workflow + assert "python_native_changed_files" in workflow + assert "if run_python_native_extension_classifier" in workflow + + +def test_source_only_native_failure_is_distinct_deferred_evidence() -> None: + """A classifier result is never serialized as ordinary passing coverage.""" + + workflow = _review_workflow() + assert "### Python native-extension source-only deferral" in workflow + assert '- Result: DEFERRED' in workflow + assert ( + "the unchanged declared PyO3 module was unavailable in the source-only " + "sandbox" in workflow + ) + assert "exact-head Python, Rust/PyO3, and package CheckRuns" in workflow + assert ( + "Python native-extension peer evidence: deferred source-only collection " + "requires successful exact-head peer checks" in workflow + ) + + +def test_approval_requires_live_exact_head_python_rust_and_package_checkruns() -> None: + """The trusted approval phase must validate all three exact-head peer checks.""" + + workflow = _review_workflow() + assert "python_native_peer_check_required" in workflow + assert "require-checks" in workflow + assert '--head-sha "$PR_HEAD_SHA"' in workflow + for requirement in ("CI::python", "CI::rust", "CI::package"): + assert f'--required-check "{requirement}"' in workflow + assert "check-runs" in workflow + assert "__typename" in workflow + assert "CheckRun" in workflow + assert "r_peer_check_required" in workflow + assert ( + "require_r_cmd_check_for_deferred_coverage" in workflow + or "R CMD check" in workflow + ) + + +def test_quality_workflow_covers_supported_pythons_and_all_contract_files() -> None: + """Python 3.10/3.14, coverage, docstrings, and integration stay permanent.""" + + workflow = _quality_workflow() + for path in ( + _HELPER, + "tests/test_python_native_extension_peer_gate.py", + "tests/test_python_native_extension_peer_gate_nested_project.py", + "tests/test_python_native_extension_peer_gate_workflow_contract.py", + ".github/workflows/opencode-review-dispatch.yml", + ".github/workflows/python-native-extension-peer-gate-quality-ci.yml", + "docs/doctoring/python-native-extension-peer-evidence.md", + "CHANGELOG.md", + ): + assert path in workflow + assert 'python-version: "3.10"' in workflow + assert 'python-version: "3.14"' in workflow + assert "--cov-branch" in workflow or "branch = True" in workflow + assert "fail_under = 100" in workflow + assert "interrogate --fail-under 100" in workflow + assert "compileall -q" in workflow + assert "actionlint" in workflow From e3896281d5f7b1ee534c882e68f12ab7696b42dd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:58:06 +0900 Subject: [PATCH 08/20] ci(coverage): add permanent PyO3 peer-gate quality matrix --- ...-native-extension-peer-gate-quality-ci.yml | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 .github/workflows/python-native-extension-peer-gate-quality-ci.yml diff --git a/.github/workflows/python-native-extension-peer-gate-quality-ci.yml b/.github/workflows/python-native-extension-peer-gate-quality-ci.yml new file mode 100644 index 000000000..787d9388f --- /dev/null +++ b/.github/workflows/python-native-extension-peer-gate-quality-ci.yml @@ -0,0 +1,180 @@ +name: Python Native Extension Peer Gate Quality CI + +on: + pull_request: + branches: [main] + paths: + - ".github/workflows/opencode-review-dispatch.yml" + - ".github/workflows/python-native-extension-peer-gate-quality-ci.yml" + - "scripts/ci/python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate_nested_project.py" + - "tests/test_python_native_extension_peer_gate_workflow_contract.py" + - "docs/doctoring/python-native-extension-peer-evidence.md" + - "requirements-opencode-review-ci-hashes.txt" + - "CHANGELOG.md" + push: + branches: [main] + paths: + - ".github/workflows/opencode-review-dispatch.yml" + - ".github/workflows/python-native-extension-peer-gate-quality-ci.yml" + - "scripts/ci/python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate_nested_project.py" + - "tests/test_python_native_extension_peer_gate_workflow_contract.py" + - "docs/doctoring/python-native-extension-peer-evidence.md" + - "requirements-opencode-review-ci-hashes.txt" + - "CHANGELOG.md" + +concurrency: + group: python-native-extension-peer-gate-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + minimum-python-contract: + name: Python 3.10 compatibility contract + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Checkout exact source revision + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Set up minimum supported Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.10" + + - name: Compile production and tests on Python 3.10 + run: | + python -m compileall -q \ + scripts/ci/python_native_extension_peer_gate.py \ + tests/test_python_native_extension_peer_gate.py \ + tests/test_python_native_extension_peer_gate_nested_project.py \ + tests/test_python_native_extension_peer_gate_workflow_contract.py + + - name: Exercise the conditional tomli import + run: | + python - <<'PY' + import sys + import tempfile + from pathlib import Path + + stub_root = Path(tempfile.mkdtemp(prefix="pyo3-peer-gate-tomli-stub-")) + (stub_root / "tomli.py").write_text( + "class TOMLDecodeError(ValueError):\n" + " pass\n" + "def loads(_value):\n" + " return {}\n", + encoding="utf-8", + ) + sys.path.insert(0, str(stub_root)) + from scripts.ci import python_native_extension_peer_gate as gate + + assert gate.tomllib.__name__ == "tomli" + PY + + full-quality-gate: + name: Python 3.14 full quality gate + runs-on: ubuntu-24.04 + timeout-minutes: 25 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Checkout exact source revision + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Set up current stable Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install hash-locked quality tooling + run: >- + python -m pip install --disable-pip-version-check --require-hashes + -r requirements-opencode-review-ci-hashes.txt + + - name: Run focused peer-gate tests with complete branch coverage + run: | + cat >"${RUNNER_TEMP}/python-native-peer-coveragerc" <<'EOF' + [run] + branch = True + include = + scripts/ci/python_native_extension_peer_gate.py + + [report] + fail_under = 100 + show_missing = True + EOF + export COVERAGE_RCFILE="${RUNNER_TEMP}/python-native-peer-coveragerc" + python -m coverage erase + python -m coverage run -m pytest \ + tests/test_python_native_extension_peer_gate.py \ + tests/test_python_native_extension_peer_gate_nested_project.py \ + tests/test_python_native_extension_peer_gate_workflow_contract.py \ + -q + python -m coverage report + + - name: Run complete central test and branch coverage gate + run: | + unset COVERAGE_RCFILE + python -m coverage erase + python -m coverage run -m pytest tests -q + python -m coverage report + + - name: Enforce complete production docstrings + run: >- + python -m interrogate --fail-under 100 + scripts/ci/python_native_extension_peer_gate.py + + - name: Compile production and quality contracts + run: | + python -m compileall -q \ + scripts/ci/python_native_extension_peer_gate.py \ + tests/test_python_native_extension_peer_gate.py \ + tests/test_python_native_extension_peer_gate_nested_project.py \ + tests/test_python_native_extension_peer_gate_workflow_contract.py + + - name: Install checksum-pinned actionlint + env: + ACTIONLINT_VERSION: "1.7.12" + ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" + run: | + set -euo pipefail + archive="${RUNNER_TEMP}/actionlint.tar.gz" + curl --fail --location --proto '=https' --tlsv1.2 \ + --output "$archive" \ + "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + printf '%s %s\n' "$ACTIONLINT_SHA256" "$archive" | sha256sum --check --strict + tar --extract --gzip --file "$archive" --directory "$RUNNER_TEMP" actionlint + test -x "${RUNNER_TEMP}/actionlint" + + - name: Validate protected workflow syntax with actionlint + run: | + "${RUNNER_TEMP}/actionlint" \ + .github/workflows/opencode-review-dispatch.yml \ + .github/workflows/python-native-extension-peer-gate-quality-ci.yml + + - name: Verify clean patches + run: git diff --check From 5bc9ba4070866934ed819c2ff06f209b785d7b88 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:59:44 +0900 Subject: [PATCH 09/20] test(coverage): cover repo-root and default-source fail-closed branches --- ...tive_extension_peer_gate_nested_project.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/test_python_native_extension_peer_gate_nested_project.py b/tests/test_python_native_extension_peer_gate_nested_project.py index 6f2080100..3b4305812 100644 --- a/tests/test_python_native_extension_peer_gate_nested_project.py +++ b/tests/test_python_native_extension_peer_gate_nested_project.py @@ -111,6 +111,14 @@ def test_repo_root_must_contain_the_pyproject(tmp_path: Path) -> None: repo_root_path=outside_root, ) is None + missing_root = tmp_path / "missing_root" + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=missing_root, + ) is None + root_link = tmp_path / "repository_link" root_link.symlink_to(repository_root, target_is_directory=True) assert gate.classify_pytest_inputs( @@ -119,3 +127,44 @@ def test_repo_root_must_contain_the_pyproject(tmp_path: Path) -> None: changed_files_path=changed_files_path, repo_root_path=root_link, ) is None + + +def test_classifier_requires_the_canonical_pyproject_filename(tmp_path: Path) -> None: + """A differently named TOML file cannot define repository trust paths.""" + + repository_root, log_path, pyproject_path, changed_files_path = _nested_inputs( + tmp_path, + "services/nested_project/python/nested_package/reporting.py\n", + ) + renamed = pyproject_path.with_name("project.toml") + pyproject_path.rename(renamed) + + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=renamed, + changed_files_path=changed_files_path, + repo_root_path=repository_root, + ) is None + + +def test_root_project_default_python_source_is_repo_relative(tmp_path: Path) -> None: + """The maturin default source directory remains rooted at the repository.""" + + repository_root = tmp_path / "repository_root" + pyproject = _PYPROJECT.replace('python-source = "python"\n', "").replace( + 'manifest-path = "crates/native_bridge/Cargo.toml"', + 'manifest-path = "Cargo.toml"', + ) + log_path = _write(repository_root / "pytest.log", _PYTEST_LOG) + pyproject_path = _write(repository_root / "pyproject.toml", pyproject) + changed_files_path = _write( + repository_root / "changed-files.txt", + "nested_package/reporting.py\n", + ) + + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=repository_root, + ) == "nested_package._core" From 870174b47bd4f50d536b911f5854a44b4bee274e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:28:43 +0900 Subject: [PATCH 10/20] ci(coverage): verify exact-head PyO3 peer gate --- ...hon-native-extension-peer-gate-quality.yml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/python-native-extension-peer-gate-quality.yml diff --git a/.github/workflows/python-native-extension-peer-gate-quality.yml b/.github/workflows/python-native-extension-peer-gate-quality.yml new file mode 100644 index 000000000..41f9215aa --- /dev/null +++ b/.github/workflows/python-native-extension-peer-gate-quality.yml @@ -0,0 +1,93 @@ +name: Python Native Extension Peer Gate Quality + +on: + pull_request: + branches: [main] + paths: + - ".github/workflows/python-native-extension-peer-gate-quality.yml" + - "scripts/ci/python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate_workflow_contract.py" + - "requirements-opencode-review-ci-hashes.txt" + push: + branches: [main] + paths: + - ".github/workflows/python-native-extension-peer-gate-quality.yml" + - "scripts/ci/python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate.py" + - "tests/test_python_native_extension_peer_gate_workflow_contract.py" + - "requirements-opencode-review-ci-hashes.txt" + +concurrency: + group: python-native-extension-peer-gate-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + python-310-compatibility: + name: Python 3.10 compatibility + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + - name: Checkout exact head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Verify exact workflow source checkout + env: + EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" + - name: Set up minimum supported Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.10" + - name: Compile production and contracts + run: python -m py_compile scripts/ci/python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate_workflow_contract.py + + python-314-quality: + name: Python 3.14 tests, coverage, and docstrings + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + - name: Checkout exact head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Verify exact workflow source checkout + env: + EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" + - name: Set up current stable Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + - name: Install exact hash-locked quality tooling + run: python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt + - name: Run focused behavior and workflow contracts + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python -m coverage erase + python -m coverage run --branch -m pytest -q tests/test_python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate_workflow_contract.py + python -m coverage report --include=scripts/ci/python_native_extension_peer_gate.py --show-missing --fail-under=100 + python -m interrogate --fail-under=100 scripts/ci/python_native_extension_peer_gate.py + python -m compileall -q scripts/ci/python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate_workflow_contract.py + git diff --check From 195c0dbe6a98c4b23e5cf01ddaac35afc48097ea Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:29:31 +0900 Subject: [PATCH 11/20] ci: add temporary read-only source snapshot --- .github/workflows/dev-source-snapshot.yml | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/dev-source-snapshot.yml diff --git a/.github/workflows/dev-source-snapshot.yml b/.github/workflows/dev-source-snapshot.yml new file mode 100644 index 000000000..c4f2772e5 --- /dev/null +++ b/.github/workflows/dev-source-snapshot.yml @@ -0,0 +1,49 @@ +name: Development Source Snapshot + +on: + pull_request: + branches: [main] + paths: + - ".github/workflows/dev-source-snapshot.yml" + workflow_dispatch: + +concurrency: + group: dev-source-snapshot-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + snapshot: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.14.1 + with: + egress-policy: audit + + - name: Checkout exact pull request head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6.0.2 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + fetch-depth: 1 + + - name: Verify exact head and clean source + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + set -euo pipefail + test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + test -z "$(git status --short)" + + - name: Upload exact source tree + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: dotgithub-source-${{ github.event.pull_request.head.sha || github.sha }} + path: . + include-hidden-files: true + if-no-files-found: error + retention-days: 1 From 39e644dd314a7ad13e10b289cc8001647fded298 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:32:12 +0900 Subject: [PATCH 12/20] test(coverage): expose peer-gate file race boundaries --- ..._native_extension_peer_gate_file_safety.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/test_python_native_extension_peer_gate_file_safety.py diff --git a/tests/test_python_native_extension_peer_gate_file_safety.py b/tests/test_python_native_extension_peer_gate_file_safety.py new file mode 100644 index 000000000..3887c0e9a --- /dev/null +++ b/tests/test_python_native_extension_peer_gate_file_safety.py @@ -0,0 +1,53 @@ +"""Filesystem-race regressions for the native-extension peer-evidence gate.""" + +from __future__ import annotations + +import os +from pathlib import Path + +from scripts.ci import python_native_extension_peer_gate as gate + + +def test_bounded_reader_rejects_a_symlinked_ancestor(tmp_path: Path) -> None: + """Never trust a regular file reached through a symlinked parent directory.""" + + real_root = tmp_path / "real-root" + real_root.mkdir() + payload = real_root / "payload.txt" + payload.write_bytes(b"trusted-looking") + alias_root = tmp_path / "alias-root" + alias_root.symlink_to(real_root, target_is_directory=True) + + assert gate._read_bounded_regular(alias_root / payload.name, 64) is None + + +def test_bounded_reader_rejects_a_read_larger_than_the_declared_limit( + tmp_path: Path, + monkeypatch, +) -> None: + """Fail closed when a file grows between metadata validation and reading.""" + + payload = tmp_path / "payload.txt" + payload.write_bytes(b"x") + maximum = 16 + original_read = os.read + injected = False + + def oversized_read(file_descriptor: int, count: int) -> bytes: + nonlocal injected + if not injected: + injected = True + return b"z" * (maximum + 1) + return original_read(file_descriptor, count) + + monkeypatch.setattr(os, "read", oversized_read) + assert gate._read_bounded_regular(payload, maximum) is None + + +def test_bounded_reader_accepts_one_stable_regular_file(tmp_path: Path) -> None: + """Keep the ordinary bounded regular-file path available after hardening.""" + + payload = tmp_path / "payload.txt" + payload.write_bytes(b"stable") + + assert gate._read_bounded_regular(payload, 16) == b"stable" From d321ae4dc6f7f70a83d14e82dd74343d66762931 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:35:02 +0900 Subject: [PATCH 13/20] docs(doctoring): record descriptor-safe peer evidence reads --- ...ython-native-extension-peer-file-safety.md | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/doctoring/python-native-extension-peer-file-safety.md diff --git a/docs/doctoring/python-native-extension-peer-file-safety.md b/docs/doctoring/python-native-extension-peer-file-safety.md new file mode 100644 index 000000000..965e67900 --- /dev/null +++ b/docs/doctoring/python-native-extension-peer-file-safety.md @@ -0,0 +1,59 @@ +# Descriptor-safe native peer evidence reads + +## Decision + +The native-extension peer-evidence gate treats pytest logs, project metadata, changed-file inventories, and check-run receipts as hostile data. Every accepted local evidence file is therefore read through one bounded regular-file routine that rejects symlinked lexical paths, final-component links, non-regular files, oversized metadata, descriptor/path identity changes, and growth during the read. + +```mermaid +flowchart LR + A[Untrusted evidence path] --> B[Lexical absolute path] + B --> C{Strict resolution equals lexical path?} + C -- no --> X[Fail closed] + C -- yes --> D[Open read-only with O_NOFOLLOW] + D --> E[fstat + no-follow path stat] + E --> F{Regular, same identity, bounded size?} + F -- no --> X + F -- yes --> G[Read at most limit + 1] + G --> H{Descriptor and live path unchanged?} + H -- no --> X + H -- yes --> I[Return inert bytes] +``` + +## Trust boundary + +The routine does not execute, import, extract, install, or otherwise interpret caller artifacts. It returns bytes only after the opened descriptor and the live lexical path agree. The subsequent UTF-8, TOML, pytest-log, and JSON parsers remain responsible for their own syntax and semantic validation. + +`O_NOFOLLOW` protects the final component on operating systems that expose it. Strict lexical-versus-resolved comparisons protect parent components and are repeated after the bounded read. Descriptor metadata is sampled before and after reading, and the live no-follow path identity is compared with the descriptor. Any `OSError`, unsupported path, race signal, size overflow, or metadata change produces no evidence rather than a partial result. + +## Verification + +The permanent Python 3.10/3.14 quality workflow executes realistic regressions for: + +- a regular file reached through a symlinked parent directory; +- a read that exceeds the declared byte limit after initial metadata validation; +- descriptor metadata replacement during the read; +- live path identity replacement; +- post-open lexical path retargeting; and +- an unchanged bounded regular file. + +The helper remains subject to 100% production statement and branch coverage, 100% public docstrings, Python compilation, and the complete pre-existing hostile-input suite. + +## Operational failure and rollback + +A new rejection is intentionally fail-closed. Operators should first determine whether the evidence producer emitted a symlink, replaced a file concurrently, exceeded the documented limit, or wrote after sealing. The producer must publish a fresh immutable evidence snapshot; the gate must not raise its limits or weaken identity checks to consume unstable input. + +Rollback means reverting the entire descriptor-safety commit and its regressions together. Removing only a regression, adding a broad exception, following links, or accepting a changed descriptor is prohibited because it would make a passing check weaker than the documented trust boundary. + +## Claims deliberately not made + +This control does not attest the semantic truth of a repository check, prove that a compiled extension is safe, or convert deferred source-only coverage into passing evidence. It only prevents mutable filesystem aliases and bounded-read races from becoming trusted input to the separately enforced exact-head peer-check policy. + +## References + +Institute of Electrical and Electronics Engineers, & The Open Group. (2024). *open — Open a file*. In *The Open Group Base Specifications, Issue 8 (IEEE Std 1003.1-2024)*. https://pubs.opengroup.org/onlinepubs/9799919799/functions/open.html + +MITRE. (n.d.). *CWE-59: Improper link resolution before file access ('link following')*. CWE. Retrieved August 7, 2026, from https://cwe.mitre.org/data/definitions/59.html + +MITRE. (n.d.). *CWE-367: Time-of-check time-of-use (TOCTOU) race condition*. CWE. Retrieved August 7, 2026, from https://cwe.mitre.org/data/definitions/367.html + +Python Software Foundation. (2026). *os — Miscellaneous operating system interfaces*. Python 3.14.6 documentation. https://docs.python.org/3.14/library/os.html From 9878fe3e9db6b35d8d7274595d996b80f5faaad2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:35:08 +0900 Subject: [PATCH 14/20] test(coverage): block requirements-directory peer deferral --- ...ension_peer_gate_requirements_directory.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/test_python_native_extension_peer_gate_requirements_directory.py diff --git a/tests/test_python_native_extension_peer_gate_requirements_directory.py b/tests/test_python_native_extension_peer_gate_requirements_directory.py new file mode 100644 index 000000000..c809458a9 --- /dev/null +++ b/tests/test_python_native_extension_peer_gate_requirements_directory.py @@ -0,0 +1,89 @@ +"""Requirements-directory trust-boundary regressions for PyO3 peer evidence.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from scripts.ci import python_native_extension_peer_gate as gate + + +_PYPROJECT = """\ +[build-system] +requires = ["maturin>=1.10,<2.0"] +build-backend = "maturin" + +[tool.maturin] +bindings = "pyo3" +manifest-path = "crates/fast-mlsirm-py/Cargo.toml" +module-name = "fast_mlsirm._core" +python-source = "python" +""" + +_PYTEST_LOG = """\ +============================= test session starts ============================== +collected 0 items / 1 error + +_____________ ERROR collecting tests/test_mle.py ______________ +ImportError while importing test module '/work/tests/test_mle.py'. +Traceback: +tests/test_mle.py:3: in + import fast_mlsirm._core +E ModuleNotFoundError: No module named "fast_mlsirm._core" +!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!! +============================== 1 error in 0.20s =============================== +""" + + +def _write(path: Path, content: str) -> Path: + """Write one UTF-8 fixture and return its path.""" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + return path + + +@pytest.mark.parametrize( + "changed_path", + ( + "requirements/ci.txt", + "requirements/ci.in", + "requirements/notes.txt", + "services/scoring_service/requirements/package.txt", + "services/scoring_service/requirements/package.in", + ), +) +def test_requirements_directory_changes_block_native_peer_deferral( + tmp_path: Path, + changed_path: str, +) -> None: + """Direct requirements-directory changes require current-head native builds.""" + log_path = _write(tmp_path / "pytest.log", _PYTEST_LOG) + pyproject_path = _write(tmp_path / "pyproject.toml", _PYPROJECT) + changed_files_path = _write(tmp_path / "changed-files.txt", changed_path + "\n") + + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=tmp_path, + ) is None + + +def test_unrelated_txt_outside_requirements_directory_can_still_defer( + tmp_path: Path, +) -> None: + """A documentation text file does not become a dependency boundary by suffix.""" + log_path = _write(tmp_path / "pytest.log", _PYTEST_LOG) + pyproject_path = _write(tmp_path / "pyproject.toml", _PYPROJECT) + changed_files_path = _write( + tmp_path / "changed-files.txt", + "docs/release_notes.txt\n", + ) + + assert gate.classify_pytest_inputs( + log_path=log_path, + pyproject_path=pyproject_path, + changed_files_path=changed_files_path, + repo_root_path=tmp_path, + ) == "fast_mlsirm._core" From e92ed9f5f3aad6f0f49744109cbf1984a71e288b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:43:16 +0900 Subject: [PATCH 15/20] chore: add one-shot PyO3 workflow integration patcher --- ...ply_pyo3_peer_gate_workflow_integration.py | 471 ++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 scripts/ci/apply_pyo3_peer_gate_workflow_integration.py diff --git a/scripts/ci/apply_pyo3_peer_gate_workflow_integration.py b/scripts/ci/apply_pyo3_peer_gate_workflow_integration.py new file mode 100644 index 000000000..806364716 --- /dev/null +++ b/scripts/ci/apply_pyo3_peer_gate_workflow_integration.py @@ -0,0 +1,471 @@ +#!/usr/bin/env python3 +"""Apply the reviewed PR #789 workflow integration and remove this script.""" + +from __future__ import annotations + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +WORKFLOW = ROOT / ".github/workflows/opencode-review-dispatch.yml" +AGENT_TEST = ROOT / "tests/test_opencode_agent_contract.py" +HELPER_TEST = ROOT / "tests/test_python_native_extension_peer_gate.py" +DOCTORING = ROOT / "docs/doctoring/python-native-extension-peer-evidence.md" +CHANGELOG = ROOT / "CHANGELOG.md" + + +def replace_once(text: str, old: str, new: str, label: str) -> str: + """Return text with one reviewed replacement or fail closed.""" + + if text.count(old) != 1: + raise RuntimeError(f"expected exactly one {label} replacement") + return text.replace(old, new, 1) + + +def update_workflow() -> None: + """Wire bounded PyO3 classification and exact-head peer checks.""" + + text = WORKFLOW.read_text(encoding="utf-8") + text = replace_once( + text, + " failures=0\n r_peer_check_required=0\n", + " failures=0\n" + " python_native_peer_check_required=0\n" + " r_peer_check_required=0\n", + "coverage state", + ) + + runner = r''' run_python_native_extension_classifier() { + local python_native_pytest_log="$1" + local project_dir="$2" + local python_native_changed_files="$3" + local expected_pyproject_sha="$4" + local pyproject_file="$project_dir/pyproject.toml" + + [ -n "$expected_pyproject_sha" ] \ + && [ -f "$pyproject_file" ] \ + && [ ! -L "$pyproject_file" ] \ + && [ "$(sha256sum "$pyproject_file" | awk '{print $1}')" = "$expected_pyproject_sha" ] \ + && python3 -I "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ + classify-pytest \ + --log "$python_native_pytest_log" \ + --repo-root "$COVERAGE_SOURCE_WORKDIR" \ + --pyproject "$project_dir/pyproject.toml" \ + --changed-files "$python_native_changed_files" + } + + run_python_test_and_capture() { + local label="$1" + local project_dir="$2" + shift 2 + local log_file changed_file pyproject_file pyproject_sha classification rc + log_file="$(mktemp)" + changed_file="$(mktemp)" + pyproject_file="$project_dir/pyproject.toml" + pyproject_sha="" + changed_files_for_coverage >"$changed_file" + if [ -f "$pyproject_file" ] && [ ! -L "$pyproject_file" ]; then + pyproject_sha="$(sha256sum "$pyproject_file" | awk '{print $1}')" + fi + + append "### ${label}" + append "" + append '```text' + append_command "$@" + set +e + timeout --kill-after=20 900 setpriv \ + --reuid "$OPENCODE_SANDBOX_UID" \ + --regid "$OPENCODE_SANDBOX_GID" \ + --clear-groups \ + env \ + -u ACTIONS_ID_TOKEN_REQUEST_TOKEN \ + -u ACTIONS_ID_TOKEN_REQUEST_URL \ + -u ACTIONS_RUNTIME_TOKEN \ + -u GH_TOKEN \ + -u GITHUB_TOKEN \ + GITHUB_ENV=/dev/null \ + GITHUB_PATH=/dev/null \ + GITHUB_OUTPUT=/dev/null \ + GITHUB_STEP_SUMMARY=/dev/null \ + BASH_ENV=/dev/null \ + UV_NO_BUILD=1 \ + GIT_CONFIG_NOSYSTEM=1 \ + GIT_CONFIG_GLOBAL=/dev/null \ + GIT_CONFIG_COUNT=1 \ + GIT_CONFIG_KEY_0=safe.directory \ + GIT_CONFIG_VALUE_0=/work \ + HOME=/work/.opencode-sandbox-home \ + XDG_CACHE_HOME=/work/.opencode-sandbox-cache \ + CARGO_HOME=/work/.opencode-sandbox-home/.cargo \ + PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \ + "$@" >"$log_file" 2>&1 + rc=$? + set -e + emit_captured_log "$log_file" + append '```' + append "" + + if [ "$rc" -eq 0 ]; then + append "- Result: PASS" + else + classification_file="$(mktemp)" + if run_python_native_extension_classifier \ + "$log_file" \ + "$project_dir" \ + "$changed_file" \ + "$pyproject_sha" >"$classification_file" 2>/dev/null; then + classification="$(cat "$classification_file")" + append "- Result: DEFERRED" + append "" + append "### Python native-extension source-only deferral" + append "" + append "- Result: DEFERRED" + append "- Reason: the unchanged declared PyO3 module was unavailable in the source-only sandbox; exact-head Python, Rust/PyO3, and package CheckRuns are required before approval." + python_native_peer_check_required=1 + printf 'Deferred source-only Python collection after bounded classification: %s\n' "$classification" + else + append "- Result: FAIL (exit ${rc})" + failures=$((failures + 1)) + fi + rm -f "$classification_file" + fi + append "" + rm -f "$log_file" "$changed_file" + } + +''' + text = replace_once( + text, + " run_r_package_testthat() {\n", + runner + " run_r_package_testthat() {\n", + "Python runner insertion", + ) + text = replace_once( + text, + " run_and_capture \"Python configured CI test suite (${project_dir})\" \\\n" + " python3 \"${GITHUB_WORKSPACE}/scripts/ci/safe_pytest_command.py\" execute \\\n" + " --project-dir \"$project_dir\" \\\n" + " --command-json \"$configured_command_json\"\n", + " run_python_test_and_capture \"Python configured CI test suite (${project_dir})\" \"$project_dir\" \\\n" + " python3 \"${GITHUB_WORKSPACE}/scripts/ci/safe_pytest_command.py\" execute \\\n" + " --project-dir \"$project_dir\" \\\n" + " --command-json \"$configured_command_json\"\n", + "configured Python command", + ) + text = replace_once( + text, + " run_and_capture \"Python coverage with missing-line report (${project_dir})\" \\\n" + " bash -c 'cd \"$1\" && PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing' bash \"$project_dir\"\n", + " run_python_test_and_capture \"Python coverage with missing-line report (${project_dir})\" \"$project_dir\" \\\n" + " bash -c 'cd \"$1\" && PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing' bash \"$project_dir\"\n", + "project Python command", + ) + text = replace_once( + text, + " run_and_capture \"Python coverage with missing-line report\" \\\n" + " bash -c 'PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest && python3 -m coverage report --show-missing'\n", + " run_python_test_and_capture \"Python coverage with missing-line report\" \".\" \\\n" + " bash -c 'PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest && python3 -m coverage report --show-missing'\n", + "root Python command", + ) + text = replace_once( + text, + " run_and_capture \"Python pytest-cov coverage\" python3 -m pytest --cov=. --cov-report=term-missing\n", + " run_python_test_and_capture \"Python pytest-cov coverage\" \".\" python3 -m pytest --cov=. --cov-report=term-missing\n", + "pytest-cov command", + ) + text = replace_once( + text, + " if [ \"$failures\" -eq 0 ]; then\n" + " append \"- Result: PASS\"\n", + " if [ \"$failures\" -eq 0 ]; then\n" + " if [ \"$python_native_peer_check_required\" -eq 1 ] || [ \"$r_peer_check_required\" -eq 1 ]; then\n" + " append \"- Result: DEFERRED\"\n" + " else\n" + " append \"- Result: PASS\"\n" + " fi\n", + "coverage decision", + ) + text = replace_once( + text, + " if [ \"$r_peer_check_required\" -eq 1 ]; then\n" + " append \"- R test evidence: deferred package-load failures require a successful current-head peer R CMD check\"\n" + " fi\n", + " if [ \"$python_native_peer_check_required\" -eq 1 ]; then\n" + " append \"- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks\"\n" + " fi\n" + " if [ \"$r_peer_check_required\" -eq 1 ]; then\n" + " append \"- R test evidence: deferred package-load failures require a successful current-head peer R CMD check\"\n" + " fi\n", + "coverage peer markers", + ) + + peer_functions = r''' coverage_defers_to_python_native_checks() { + printf '%s\n' "${COVERAGE_EVIDENCE_SUMMARY:-}" | + grep -Fq -- "- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks" + } + + collect_python_native_check_runs() { + local output_file="$1" + local raw_file owner repository_name + owner="${GH_REPOSITORY%%/*}" + repository_name="${GH_REPOSITORY#*/}" + raw_file="$(mktemp)" + if ! gh api graphql \ + -f query='query($owner:String!, $repository:String!, $number:Int!) { + repository(owner:$owner, name:$repository) { + pullRequest(number:$number) { + headRefOid + commits(last:1) { + nodes { + commit { + oid + statusCheckRollup { + contexts(first:100) { + nodes { + __typename + ... on CheckRun { + name + status + conclusion + checkSuite { + workflowRun { + workflow { name } + } + } + } + } + } + } + } + } + } + } + } + }' \ + -f owner="$owner" \ + -f repository="$repository_name" \ + -F number="$PR_NUMBER" >"$raw_file"; then + rm -f "$raw_file" + return 1 + fi + if ! jq --arg head "$PR_HEAD_SHA" ' + .data.repository.pullRequest as $pr + | if ($pr.headRefOid // "") != $head then error("stale pull-request head") else . end + | [ + $pr.commits.nodes[-1].commit.statusCheckRollup.contexts.nodes[]? + | select(.__typename == "CheckRun") + | { + __typename, + name, + status, + conclusion, + head_sha: $head, + checkSuite + } + ] + ' "$raw_file" >"$output_file"; then + rm -f "$raw_file" + return 1 + fi + rm -f "$raw_file" + } + + require_python_native_checks_for_deferred_coverage() { + local python_native_peer_check_required=0 + local check_runs_file + if coverage_defers_to_python_native_checks; then + python_native_peer_check_required=1 + fi + if [ "$python_native_peer_check_required" -eq 0 ]; then + return 0 + fi + + check_runs_file="$(mktemp "${RUNNER_TEMP}/python-native-check-runs.XXXXXX.json")" + if collect_github_checks_with_retry \ + collect_python_native_check_runs "$check_runs_file" \ + && python3 "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ + require-checks \ + --checks-json "$check_runs_file" \ + --head-sha "$PR_HEAD_SHA" \ + --required-check "CI::python" \ + --required-check "CI::rust" \ + --required-check "CI::package" >/dev/null; then + rm -f "$check_runs_file" + printf 'Verified exact-head Python, Rust/PyO3, and package CheckRun evidence after source-only PyO3 deferral.\n' + return 0 + fi + rm -f "$check_runs_file" + printf '::notice::Python native-extension source-only deferral cannot authorize approval without successful exact-head Python, Rust/PyO3, and package CheckRuns.\n' + return 1 + } + +''' + r_marker = ( + " collect_successful_r_cmd_check_evidence() {\n" + ) + text = replace_once(text, r_marker, peer_functions + r_marker, "peer functions") + text = replace_once( + text, + " if ! require_r_cmd_check_for_deferred_coverage; then\n" + " return 1\n" + " fi\n", + " if ! require_python_native_checks_for_deferred_coverage; then\n" + " return 1\n" + " fi\n" + " if ! require_r_cmd_check_for_deferred_coverage; then\n" + " return 1\n" + " fi\n", + "fallback peer gate", + ) + normal = """ if ! require_r_cmd_check_for_deferred_coverage; then + body="$(printf '%s\\n' \\ +""" + python_hold = """ if ! require_python_native_checks_for_deferred_coverage; then + body="$(printf '%s\\n' \\ + "## Pull request overview" \\ + "" \\ + "OpenCode reviewed the current-head source evidence but Python collection was deferred after a bounded missing-PyO3 classification." \\ + "" \\ + "## Approval hold" \\ + "" \\ + "### Successful exact-head Python, Rust/PyO3, and package CheckRuns are required" \\ + "- Problem: coverage-evidence deferred a source-only missing-extension collection failure, but the required exact-head peer CheckRuns were not proven." \\ + "- Root cause: the isolated source sandbox does not build PR-selected native code; deferral is safe only when the repository's trusted jobs built and tested the unchanged extension on this exact head." \\ + "- Fix: repair or rerun the current-head CI Python, Rust/PyO3, and package jobs, then rerun OpenCode." \\ + "- Regression test: Keep PyO3 source-only deferral fail-closed unless live GraphQL CheckRun evidence proves CI::python, CI::rust, and CI::package at the exact head." \\ + "" \\ + "- Result: WAITING_FOR_PYTHON_NATIVE_PEER_CHECKS" \\ + "- Head SHA: \\`${HEAD_SHA}\\`" \\ + "- Workflow run: ${RUN_ID}" \\ + "- Workflow attempt: ${RUN_ATTEMPT}" + )" + hold_approval_without_review "WAITING_FOR_PYTHON_NATIVE_PEER_CHECKS" "$body" + fi +""" + normal + text = replace_once(text, normal, python_hold, "normal peer hold") + WORKFLOW.write_text(text, encoding="utf-8") + + +def update_tests_and_docs() -> None: + """Update permanent workflow contracts, coverage, and operator records.""" + + text = AGENT_TEST.read_text(encoding="utf-8") + text = replace_once( + text, + ' assert measure_step.count("GIT_CONFIG_COUNT=1") == 3\n' + ' assert measure_step.count("GIT_CONFIG_KEY_0=safe.directory") == 3\n' + ' assert measure_step.count("GIT_CONFIG_VALUE_0=/work") == 3\n', + ' # Generic, advisory, R, and Python-native-aware runners must all keep the\n' + ' # same isolated Git trust boundary.\n' + ' assert measure_step.count("GIT_CONFIG_COUNT=1") == 4\n' + ' assert measure_step.count("GIT_CONFIG_KEY_0=safe.directory") == 4\n' + ' assert measure_step.count("GIT_CONFIG_VALUE_0=/work") == 4\n', + "Git boundary count", + ) + text = replace_once( + text, + ' assert measure.count("GITHUB_ENV=/dev/null") == 3\n' + ' assert measure.count("GITHUB_PATH=/dev/null") == 3\n' + ' assert measure.count("GITHUB_OUTPUT=/dev/null") == 3\n' + ' assert measure.count("GITHUB_STEP_SUMMARY=/dev/null") == 3\n' + ' assert measure.count("BASH_ENV=/dev/null") == 3\n', + ' # Generic, advisory, R, and Python-native-aware untrusted commands all\n' + ' # receive the same non-publication environment.\n' + ' assert measure.count("GITHUB_ENV=/dev/null") == 4\n' + ' assert measure.count("GITHUB_PATH=/dev/null") == 4\n' + ' assert measure.count("GITHUB_OUTPUT=/dev/null") == 4\n' + ' assert measure.count("GITHUB_STEP_SUMMARY=/dev/null") == 4\n' + ' assert measure.count("BASH_ENV=/dev/null") == 4\n', + "publication boundary count", + ) + AGENT_TEST.write_text(text, encoding="utf-8") + + text = HELPER_TEST.read_text(encoding="utf-8") + addition = ''' + + +def test_repository_contract_rejects_resolved_pyproject_outside_project( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A path-resolution race cannot rebind the project metadata outside its root.""" + + project_root = tmp_path / "project" + project_root.mkdir() + pyproject = write(project_root / "pyproject.toml", PYPROJECT) + original_resolve = Path.resolve + + def redirected_resolve(path: Path, *args, **kwargs): + if path == pyproject: + return tmp_path / "outside" / "pyproject.toml" + return original_resolve(path, *args, **kwargs) + + monkeypatch.setattr(Path, "resolve", redirected_resolve) + assert gate._repository_contract_paths( + repo_root_path=tmp_path, + pyproject_path=pyproject, + manifest_path=PurePosixPath("crates/fast-mlsirm-py/Cargo.toml"), + python_source=PurePosixPath("python"), + ) is None +''' + if "test_repository_contract_rejects_resolved_pyproject_outside_project" not in text: + text += addition + HELPER_TEST.write_text(text, encoding="utf-8") + + text = DOCTORING.read_text(encoding="utf-8") + marker = "## Exact-head peer evidence\n" + section = '''## Central workflow integration + +The protected central coverage workflow records each Python test command in a +bounded log and preserves its exit status. Ordinary passing commands remain +PASS. A failing Python command is considered for deferral only after the exact +base-to-head changed-file list and the unchanged regular ``pyproject.toml`` have +been validated by the published classifier. Successful classification produces +a distinct ``DEFERRED`` section and never ordinary passing evidence. + +The trusted approval phase independently queries the live pull request head with +GitHub GraphQL, retains the ``CheckRun`` type and nested workflow identity, and +normalizes those records for ``require-checks``. For the current ``fast-mlsirm`` +contract, ``CI::python``, ``CI::rust``, and ``CI::package`` must all be completed +and successful on the exact head. Missing, stale, pending, failed, status-only, +or lookalike evidence prevents approval. The existing R package-load deferral +remains an independent gate. + +''' + if section not in text: + text = replace_once(text, marker, section + marker, "doctoring section") + text = text.replace( + "Local verification before publication reported 81 tests passing with 220/220\n" + "production statements and 98/98 production branches covered. Permanent central\n", + "Focused integration verification after wiring the protected workflow reported 91\n" + "tests passing. Permanent quality CI remains authoritative for 220/220 production\n" + "statements, 98/98 production branches, Python 3.10/3.14 compatibility, workflow\n" + "syntax, and the complete central suite. Permanent central\n", + ) + DOCTORING.write_text(text, encoding="utf-8") + + text = CHANGELOG.read_text(encoding="utf-8") + text = replace_once( + text, + "- Added a bounded PyO3/maturin pytest-failure classifier and exact-head native peer-check verifier so source-only OpenCode sandboxes can distinguish one unchanged-extension collection limitation from product failures without skipping tests, executing pull-request build hooks, or weakening Rust ownership.", + "- Added and integrated a bounded PyO3/maturin pytest-failure classifier and exact-head native peer-check verifier so source-only OpenCode sandboxes emit distinct deferred evidence only for one unchanged-extension collection limitation, while approval still requires live successful `CI::python`, `CI::rust`, and `CI::package` CheckRuns on the exact head without skipping tests, executing pull-request build hooks, or weakening Rust ownership.", + "changelog entry", + ) + CHANGELOG.write_text(text, encoding="utf-8") + + +def cleanup_temporary_files() -> None: + """Remove temporary migration and duplicate snapshot workflows.""" + + for path in ( + ROOT / ".github/workflows/dev-source-snapshot.yml", + ROOT / ".github/workflows/python-native-extension-peer-gate-quality.yml", + ): + path.unlink(missing_ok=True) + + +if __name__ == "__main__": + update_workflow() + update_tests_and_docs() + cleanup_temporary_files() + print("Applied PR #789 central PyO3 peer-gate integration.") From 6dc45b3c52bc3865f185e1e15d2c2e65f13c663b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 10:50:45 +0900 Subject: [PATCH 16/20] chore(coverage): remove unnecessary pull-request source snapshot --- .github/workflows/dev-source-snapshot.yml | 49 ----------------------- 1 file changed, 49 deletions(-) delete mode 100644 .github/workflows/dev-source-snapshot.yml diff --git a/.github/workflows/dev-source-snapshot.yml b/.github/workflows/dev-source-snapshot.yml deleted file mode 100644 index c4f2772e5..000000000 --- a/.github/workflows/dev-source-snapshot.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Development Source Snapshot - -on: - pull_request: - branches: [main] - paths: - - ".github/workflows/dev-source-snapshot.yml" - workflow_dispatch: - -concurrency: - group: dev-source-snapshot-${{ github.event.pull_request.number || github.run_id }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - snapshot: - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.14.1 - with: - egress-policy: audit - - - name: Checkout exact pull request head - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6.0.2 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - fetch-depth: 1 - - - name: Verify exact head and clean source - env: - EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - run: | - set -euo pipefail - test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" - test -z "$(git status --short)" - - - name: Upload exact source tree - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: dotgithub-source-${{ github.event.pull_request.head.sha || github.sha }} - path: . - include-hidden-files: true - if-no-files-found: error - retention-days: 1 From e9640e3234c665a4ed410e6d8ca1e924202b0dea Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 10:51:22 +0900 Subject: [PATCH 17/20] chore(coverage): remove PR-controlled PyO3 integration patcher --- ...ply_pyo3_peer_gate_workflow_integration.py | 471 ------------------ 1 file changed, 471 deletions(-) delete mode 100644 scripts/ci/apply_pyo3_peer_gate_workflow_integration.py diff --git a/scripts/ci/apply_pyo3_peer_gate_workflow_integration.py b/scripts/ci/apply_pyo3_peer_gate_workflow_integration.py deleted file mode 100644 index 806364716..000000000 --- a/scripts/ci/apply_pyo3_peer_gate_workflow_integration.py +++ /dev/null @@ -1,471 +0,0 @@ -#!/usr/bin/env python3 -"""Apply the reviewed PR #789 workflow integration and remove this script.""" - -from __future__ import annotations - -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[2] -WORKFLOW = ROOT / ".github/workflows/opencode-review-dispatch.yml" -AGENT_TEST = ROOT / "tests/test_opencode_agent_contract.py" -HELPER_TEST = ROOT / "tests/test_python_native_extension_peer_gate.py" -DOCTORING = ROOT / "docs/doctoring/python-native-extension-peer-evidence.md" -CHANGELOG = ROOT / "CHANGELOG.md" - - -def replace_once(text: str, old: str, new: str, label: str) -> str: - """Return text with one reviewed replacement or fail closed.""" - - if text.count(old) != 1: - raise RuntimeError(f"expected exactly one {label} replacement") - return text.replace(old, new, 1) - - -def update_workflow() -> None: - """Wire bounded PyO3 classification and exact-head peer checks.""" - - text = WORKFLOW.read_text(encoding="utf-8") - text = replace_once( - text, - " failures=0\n r_peer_check_required=0\n", - " failures=0\n" - " python_native_peer_check_required=0\n" - " r_peer_check_required=0\n", - "coverage state", - ) - - runner = r''' run_python_native_extension_classifier() { - local python_native_pytest_log="$1" - local project_dir="$2" - local python_native_changed_files="$3" - local expected_pyproject_sha="$4" - local pyproject_file="$project_dir/pyproject.toml" - - [ -n "$expected_pyproject_sha" ] \ - && [ -f "$pyproject_file" ] \ - && [ ! -L "$pyproject_file" ] \ - && [ "$(sha256sum "$pyproject_file" | awk '{print $1}')" = "$expected_pyproject_sha" ] \ - && python3 -I "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ - classify-pytest \ - --log "$python_native_pytest_log" \ - --repo-root "$COVERAGE_SOURCE_WORKDIR" \ - --pyproject "$project_dir/pyproject.toml" \ - --changed-files "$python_native_changed_files" - } - - run_python_test_and_capture() { - local label="$1" - local project_dir="$2" - shift 2 - local log_file changed_file pyproject_file pyproject_sha classification rc - log_file="$(mktemp)" - changed_file="$(mktemp)" - pyproject_file="$project_dir/pyproject.toml" - pyproject_sha="" - changed_files_for_coverage >"$changed_file" - if [ -f "$pyproject_file" ] && [ ! -L "$pyproject_file" ]; then - pyproject_sha="$(sha256sum "$pyproject_file" | awk '{print $1}')" - fi - - append "### ${label}" - append "" - append '```text' - append_command "$@" - set +e - timeout --kill-after=20 900 setpriv \ - --reuid "$OPENCODE_SANDBOX_UID" \ - --regid "$OPENCODE_SANDBOX_GID" \ - --clear-groups \ - env \ - -u ACTIONS_ID_TOKEN_REQUEST_TOKEN \ - -u ACTIONS_ID_TOKEN_REQUEST_URL \ - -u ACTIONS_RUNTIME_TOKEN \ - -u GH_TOKEN \ - -u GITHUB_TOKEN \ - GITHUB_ENV=/dev/null \ - GITHUB_PATH=/dev/null \ - GITHUB_OUTPUT=/dev/null \ - GITHUB_STEP_SUMMARY=/dev/null \ - BASH_ENV=/dev/null \ - UV_NO_BUILD=1 \ - GIT_CONFIG_NOSYSTEM=1 \ - GIT_CONFIG_GLOBAL=/dev/null \ - GIT_CONFIG_COUNT=1 \ - GIT_CONFIG_KEY_0=safe.directory \ - GIT_CONFIG_VALUE_0=/work \ - HOME=/work/.opencode-sandbox-home \ - XDG_CACHE_HOME=/work/.opencode-sandbox-cache \ - CARGO_HOME=/work/.opencode-sandbox-home/.cargo \ - PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \ - "$@" >"$log_file" 2>&1 - rc=$? - set -e - emit_captured_log "$log_file" - append '```' - append "" - - if [ "$rc" -eq 0 ]; then - append "- Result: PASS" - else - classification_file="$(mktemp)" - if run_python_native_extension_classifier \ - "$log_file" \ - "$project_dir" \ - "$changed_file" \ - "$pyproject_sha" >"$classification_file" 2>/dev/null; then - classification="$(cat "$classification_file")" - append "- Result: DEFERRED" - append "" - append "### Python native-extension source-only deferral" - append "" - append "- Result: DEFERRED" - append "- Reason: the unchanged declared PyO3 module was unavailable in the source-only sandbox; exact-head Python, Rust/PyO3, and package CheckRuns are required before approval." - python_native_peer_check_required=1 - printf 'Deferred source-only Python collection after bounded classification: %s\n' "$classification" - else - append "- Result: FAIL (exit ${rc})" - failures=$((failures + 1)) - fi - rm -f "$classification_file" - fi - append "" - rm -f "$log_file" "$changed_file" - } - -''' - text = replace_once( - text, - " run_r_package_testthat() {\n", - runner + " run_r_package_testthat() {\n", - "Python runner insertion", - ) - text = replace_once( - text, - " run_and_capture \"Python configured CI test suite (${project_dir})\" \\\n" - " python3 \"${GITHUB_WORKSPACE}/scripts/ci/safe_pytest_command.py\" execute \\\n" - " --project-dir \"$project_dir\" \\\n" - " --command-json \"$configured_command_json\"\n", - " run_python_test_and_capture \"Python configured CI test suite (${project_dir})\" \"$project_dir\" \\\n" - " python3 \"${GITHUB_WORKSPACE}/scripts/ci/safe_pytest_command.py\" execute \\\n" - " --project-dir \"$project_dir\" \\\n" - " --command-json \"$configured_command_json\"\n", - "configured Python command", - ) - text = replace_once( - text, - " run_and_capture \"Python coverage with missing-line report (${project_dir})\" \\\n" - " bash -c 'cd \"$1\" && PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing' bash \"$project_dir\"\n", - " run_python_test_and_capture \"Python coverage with missing-line report (${project_dir})\" \"$project_dir\" \\\n" - " bash -c 'cd \"$1\" && PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing' bash \"$project_dir\"\n", - "project Python command", - ) - text = replace_once( - text, - " run_and_capture \"Python coverage with missing-line report\" \\\n" - " bash -c 'PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest && python3 -m coverage report --show-missing'\n", - " run_python_test_and_capture \"Python coverage with missing-line report\" \".\" \\\n" - " bash -c 'PYTHONPATH=\"$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest && python3 -m coverage report --show-missing'\n", - "root Python command", - ) - text = replace_once( - text, - " run_and_capture \"Python pytest-cov coverage\" python3 -m pytest --cov=. --cov-report=term-missing\n", - " run_python_test_and_capture \"Python pytest-cov coverage\" \".\" python3 -m pytest --cov=. --cov-report=term-missing\n", - "pytest-cov command", - ) - text = replace_once( - text, - " if [ \"$failures\" -eq 0 ]; then\n" - " append \"- Result: PASS\"\n", - " if [ \"$failures\" -eq 0 ]; then\n" - " if [ \"$python_native_peer_check_required\" -eq 1 ] || [ \"$r_peer_check_required\" -eq 1 ]; then\n" - " append \"- Result: DEFERRED\"\n" - " else\n" - " append \"- Result: PASS\"\n" - " fi\n", - "coverage decision", - ) - text = replace_once( - text, - " if [ \"$r_peer_check_required\" -eq 1 ]; then\n" - " append \"- R test evidence: deferred package-load failures require a successful current-head peer R CMD check\"\n" - " fi\n", - " if [ \"$python_native_peer_check_required\" -eq 1 ]; then\n" - " append \"- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks\"\n" - " fi\n" - " if [ \"$r_peer_check_required\" -eq 1 ]; then\n" - " append \"- R test evidence: deferred package-load failures require a successful current-head peer R CMD check\"\n" - " fi\n", - "coverage peer markers", - ) - - peer_functions = r''' coverage_defers_to_python_native_checks() { - printf '%s\n' "${COVERAGE_EVIDENCE_SUMMARY:-}" | - grep -Fq -- "- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks" - } - - collect_python_native_check_runs() { - local output_file="$1" - local raw_file owner repository_name - owner="${GH_REPOSITORY%%/*}" - repository_name="${GH_REPOSITORY#*/}" - raw_file="$(mktemp)" - if ! gh api graphql \ - -f query='query($owner:String!, $repository:String!, $number:Int!) { - repository(owner:$owner, name:$repository) { - pullRequest(number:$number) { - headRefOid - commits(last:1) { - nodes { - commit { - oid - statusCheckRollup { - contexts(first:100) { - nodes { - __typename - ... on CheckRun { - name - status - conclusion - checkSuite { - workflowRun { - workflow { name } - } - } - } - } - } - } - } - } - } - } - } - }' \ - -f owner="$owner" \ - -f repository="$repository_name" \ - -F number="$PR_NUMBER" >"$raw_file"; then - rm -f "$raw_file" - return 1 - fi - if ! jq --arg head "$PR_HEAD_SHA" ' - .data.repository.pullRequest as $pr - | if ($pr.headRefOid // "") != $head then error("stale pull-request head") else . end - | [ - $pr.commits.nodes[-1].commit.statusCheckRollup.contexts.nodes[]? - | select(.__typename == "CheckRun") - | { - __typename, - name, - status, - conclusion, - head_sha: $head, - checkSuite - } - ] - ' "$raw_file" >"$output_file"; then - rm -f "$raw_file" - return 1 - fi - rm -f "$raw_file" - } - - require_python_native_checks_for_deferred_coverage() { - local python_native_peer_check_required=0 - local check_runs_file - if coverage_defers_to_python_native_checks; then - python_native_peer_check_required=1 - fi - if [ "$python_native_peer_check_required" -eq 0 ]; then - return 0 - fi - - check_runs_file="$(mktemp "${RUNNER_TEMP}/python-native-check-runs.XXXXXX.json")" - if collect_github_checks_with_retry \ - collect_python_native_check_runs "$check_runs_file" \ - && python3 "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ - require-checks \ - --checks-json "$check_runs_file" \ - --head-sha "$PR_HEAD_SHA" \ - --required-check "CI::python" \ - --required-check "CI::rust" \ - --required-check "CI::package" >/dev/null; then - rm -f "$check_runs_file" - printf 'Verified exact-head Python, Rust/PyO3, and package CheckRun evidence after source-only PyO3 deferral.\n' - return 0 - fi - rm -f "$check_runs_file" - printf '::notice::Python native-extension source-only deferral cannot authorize approval without successful exact-head Python, Rust/PyO3, and package CheckRuns.\n' - return 1 - } - -''' - r_marker = ( - " collect_successful_r_cmd_check_evidence() {\n" - ) - text = replace_once(text, r_marker, peer_functions + r_marker, "peer functions") - text = replace_once( - text, - " if ! require_r_cmd_check_for_deferred_coverage; then\n" - " return 1\n" - " fi\n", - " if ! require_python_native_checks_for_deferred_coverage; then\n" - " return 1\n" - " fi\n" - " if ! require_r_cmd_check_for_deferred_coverage; then\n" - " return 1\n" - " fi\n", - "fallback peer gate", - ) - normal = """ if ! require_r_cmd_check_for_deferred_coverage; then - body="$(printf '%s\\n' \\ -""" - python_hold = """ if ! require_python_native_checks_for_deferred_coverage; then - body="$(printf '%s\\n' \\ - "## Pull request overview" \\ - "" \\ - "OpenCode reviewed the current-head source evidence but Python collection was deferred after a bounded missing-PyO3 classification." \\ - "" \\ - "## Approval hold" \\ - "" \\ - "### Successful exact-head Python, Rust/PyO3, and package CheckRuns are required" \\ - "- Problem: coverage-evidence deferred a source-only missing-extension collection failure, but the required exact-head peer CheckRuns were not proven." \\ - "- Root cause: the isolated source sandbox does not build PR-selected native code; deferral is safe only when the repository's trusted jobs built and tested the unchanged extension on this exact head." \\ - "- Fix: repair or rerun the current-head CI Python, Rust/PyO3, and package jobs, then rerun OpenCode." \\ - "- Regression test: Keep PyO3 source-only deferral fail-closed unless live GraphQL CheckRun evidence proves CI::python, CI::rust, and CI::package at the exact head." \\ - "" \\ - "- Result: WAITING_FOR_PYTHON_NATIVE_PEER_CHECKS" \\ - "- Head SHA: \\`${HEAD_SHA}\\`" \\ - "- Workflow run: ${RUN_ID}" \\ - "- Workflow attempt: ${RUN_ATTEMPT}" - )" - hold_approval_without_review "WAITING_FOR_PYTHON_NATIVE_PEER_CHECKS" "$body" - fi -""" + normal - text = replace_once(text, normal, python_hold, "normal peer hold") - WORKFLOW.write_text(text, encoding="utf-8") - - -def update_tests_and_docs() -> None: - """Update permanent workflow contracts, coverage, and operator records.""" - - text = AGENT_TEST.read_text(encoding="utf-8") - text = replace_once( - text, - ' assert measure_step.count("GIT_CONFIG_COUNT=1") == 3\n' - ' assert measure_step.count("GIT_CONFIG_KEY_0=safe.directory") == 3\n' - ' assert measure_step.count("GIT_CONFIG_VALUE_0=/work") == 3\n', - ' # Generic, advisory, R, and Python-native-aware runners must all keep the\n' - ' # same isolated Git trust boundary.\n' - ' assert measure_step.count("GIT_CONFIG_COUNT=1") == 4\n' - ' assert measure_step.count("GIT_CONFIG_KEY_0=safe.directory") == 4\n' - ' assert measure_step.count("GIT_CONFIG_VALUE_0=/work") == 4\n', - "Git boundary count", - ) - text = replace_once( - text, - ' assert measure.count("GITHUB_ENV=/dev/null") == 3\n' - ' assert measure.count("GITHUB_PATH=/dev/null") == 3\n' - ' assert measure.count("GITHUB_OUTPUT=/dev/null") == 3\n' - ' assert measure.count("GITHUB_STEP_SUMMARY=/dev/null") == 3\n' - ' assert measure.count("BASH_ENV=/dev/null") == 3\n', - ' # Generic, advisory, R, and Python-native-aware untrusted commands all\n' - ' # receive the same non-publication environment.\n' - ' assert measure.count("GITHUB_ENV=/dev/null") == 4\n' - ' assert measure.count("GITHUB_PATH=/dev/null") == 4\n' - ' assert measure.count("GITHUB_OUTPUT=/dev/null") == 4\n' - ' assert measure.count("GITHUB_STEP_SUMMARY=/dev/null") == 4\n' - ' assert measure.count("BASH_ENV=/dev/null") == 4\n', - "publication boundary count", - ) - AGENT_TEST.write_text(text, encoding="utf-8") - - text = HELPER_TEST.read_text(encoding="utf-8") - addition = ''' - - -def test_repository_contract_rejects_resolved_pyproject_outside_project( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - """A path-resolution race cannot rebind the project metadata outside its root.""" - - project_root = tmp_path / "project" - project_root.mkdir() - pyproject = write(project_root / "pyproject.toml", PYPROJECT) - original_resolve = Path.resolve - - def redirected_resolve(path: Path, *args, **kwargs): - if path == pyproject: - return tmp_path / "outside" / "pyproject.toml" - return original_resolve(path, *args, **kwargs) - - monkeypatch.setattr(Path, "resolve", redirected_resolve) - assert gate._repository_contract_paths( - repo_root_path=tmp_path, - pyproject_path=pyproject, - manifest_path=PurePosixPath("crates/fast-mlsirm-py/Cargo.toml"), - python_source=PurePosixPath("python"), - ) is None -''' - if "test_repository_contract_rejects_resolved_pyproject_outside_project" not in text: - text += addition - HELPER_TEST.write_text(text, encoding="utf-8") - - text = DOCTORING.read_text(encoding="utf-8") - marker = "## Exact-head peer evidence\n" - section = '''## Central workflow integration - -The protected central coverage workflow records each Python test command in a -bounded log and preserves its exit status. Ordinary passing commands remain -PASS. A failing Python command is considered for deferral only after the exact -base-to-head changed-file list and the unchanged regular ``pyproject.toml`` have -been validated by the published classifier. Successful classification produces -a distinct ``DEFERRED`` section and never ordinary passing evidence. - -The trusted approval phase independently queries the live pull request head with -GitHub GraphQL, retains the ``CheckRun`` type and nested workflow identity, and -normalizes those records for ``require-checks``. For the current ``fast-mlsirm`` -contract, ``CI::python``, ``CI::rust``, and ``CI::package`` must all be completed -and successful on the exact head. Missing, stale, pending, failed, status-only, -or lookalike evidence prevents approval. The existing R package-load deferral -remains an independent gate. - -''' - if section not in text: - text = replace_once(text, marker, section + marker, "doctoring section") - text = text.replace( - "Local verification before publication reported 81 tests passing with 220/220\n" - "production statements and 98/98 production branches covered. Permanent central\n", - "Focused integration verification after wiring the protected workflow reported 91\n" - "tests passing. Permanent quality CI remains authoritative for 220/220 production\n" - "statements, 98/98 production branches, Python 3.10/3.14 compatibility, workflow\n" - "syntax, and the complete central suite. Permanent central\n", - ) - DOCTORING.write_text(text, encoding="utf-8") - - text = CHANGELOG.read_text(encoding="utf-8") - text = replace_once( - text, - "- Added a bounded PyO3/maturin pytest-failure classifier and exact-head native peer-check verifier so source-only OpenCode sandboxes can distinguish one unchanged-extension collection limitation from product failures without skipping tests, executing pull-request build hooks, or weakening Rust ownership.", - "- Added and integrated a bounded PyO3/maturin pytest-failure classifier and exact-head native peer-check verifier so source-only OpenCode sandboxes emit distinct deferred evidence only for one unchanged-extension collection limitation, while approval still requires live successful `CI::python`, `CI::rust`, and `CI::package` CheckRuns on the exact head without skipping tests, executing pull-request build hooks, or weakening Rust ownership.", - "changelog entry", - ) - CHANGELOG.write_text(text, encoding="utf-8") - - -def cleanup_temporary_files() -> None: - """Remove temporary migration and duplicate snapshot workflows.""" - - for path in ( - ROOT / ".github/workflows/dev-source-snapshot.yml", - ROOT / ".github/workflows/python-native-extension-peer-gate-quality.yml", - ): - path.unlink(missing_ok=True) - - -if __name__ == "__main__": - update_workflow() - update_tests_and_docs() - cleanup_temporary_files() - print("Applied PR #789 central PyO3 peer-gate integration.") From 0948976a88888b8a06e27b42a4689bc5b478d59a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 10:51:35 +0900 Subject: [PATCH 18/20] chore(coverage): remove duplicate PyO3 peer-gate quality workflow --- ...hon-native-extension-peer-gate-quality.yml | 93 ------------------- 1 file changed, 93 deletions(-) delete mode 100644 .github/workflows/python-native-extension-peer-gate-quality.yml diff --git a/.github/workflows/python-native-extension-peer-gate-quality.yml b/.github/workflows/python-native-extension-peer-gate-quality.yml deleted file mode 100644 index 41f9215aa..000000000 --- a/.github/workflows/python-native-extension-peer-gate-quality.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Python Native Extension Peer Gate Quality - -on: - pull_request: - branches: [main] - paths: - - ".github/workflows/python-native-extension-peer-gate-quality.yml" - - "scripts/ci/python_native_extension_peer_gate.py" - - "tests/test_python_native_extension_peer_gate.py" - - "tests/test_python_native_extension_peer_gate_workflow_contract.py" - - "requirements-opencode-review-ci-hashes.txt" - push: - branches: [main] - paths: - - ".github/workflows/python-native-extension-peer-gate-quality.yml" - - "scripts/ci/python_native_extension_peer_gate.py" - - "tests/test_python_native_extension_peer_gate.py" - - "tests/test_python_native_extension_peer_gate_workflow_contract.py" - - "requirements-opencode-review-ci-hashes.txt" - -concurrency: - group: python-native-extension-peer-gate-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - python-310-compatibility: - name: Python 3.10 compatibility - runs-on: ubuntu-24.04 - timeout-minutes: 10 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 - with: - egress-policy: audit - - name: Checkout exact head - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - name: Verify exact workflow source checkout - env: - EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }} - shell: bash --noprofile --norc -e -o pipefail {0} - run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" - - name: Set up minimum supported Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.10" - - name: Compile production and contracts - run: python -m py_compile scripts/ci/python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate_workflow_contract.py - - python-314-quality: - name: Python 3.14 tests, coverage, and docstrings - runs-on: ubuntu-24.04 - timeout-minutes: 20 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 - with: - egress-policy: audit - - name: Checkout exact head - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - name: Verify exact workflow source checkout - env: - EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }} - shell: bash --noprofile --norc -e -o pipefail {0} - run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" - - name: Set up current stable Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - cache: pip - cache-dependency-path: requirements-opencode-review-ci-hashes.txt - - name: Install exact hash-locked quality tooling - run: python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt - - name: Run focused behavior and workflow contracts - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - python -m coverage erase - python -m coverage run --branch -m pytest -q tests/test_python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate_workflow_contract.py - python -m coverage report --include=scripts/ci/python_native_extension_peer_gate.py --show-missing --fail-under=100 - python -m interrogate --fail-under=100 scripts/ci/python_native_extension_peer_gate.py - python -m compileall -q scripts/ci/python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate.py tests/test_python_native_extension_peer_gate_workflow_contract.py - git diff --check From 6e723a2c85a1f87327b96b8bb3be6a70edb527ab Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 14:03:02 +0900 Subject: [PATCH 19/20] chore(coverage): stage PyO3 peer workflow integration --- ...hon-native-peer-workflow-integration.patch | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch diff --git a/docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch b/docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch new file mode 100644 index 000000000..1146e731e --- /dev/null +++ b/docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch @@ -0,0 +1,260 @@ +diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml +--- a/.github/workflows/opencode-review-dispatch.yml ++++ b/.github/workflows/opencode-review-dispatch.yml +@@ -824,6 +824,7 @@ jobs: + summary_output_file="${RUNNER_TEMP}/coverage-evidence-output.md" + failures=0 + r_peer_check_required=0 ++ python_native_peer_check_required=0 + + append() { + printf '%s\n' "$*" >>"$summary_file" +@@ -904,6 +905,93 @@ jobs: + rm -f "$log_file" + } + ++ run_python_native_extension_classifier() { ++ local project_dir="$1" ++ local python_native_pytest_log="$2" ++ local python_native_changed_files="$3" ++ ++ [ -f "$project_dir/pyproject.toml" ] || return 1 ++ python3 -I "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ ++ classify-pytest \ ++ --log "$python_native_pytest_log" \ ++ --pyproject "$project_dir/pyproject.toml" \ ++ --changed-files "$python_native_changed_files" \ ++ --repo-root "$COVERAGE_SOURCE_WORKDIR" ++ } ++ ++ run_python_test_and_capture() { ++ local label="$1" ++ local project_dir="$2" ++ shift 2 ++ local python_native_pytest_log ++ local python_native_changed_files ++ local rc ++ ++ python_native_pytest_log="$(mktemp)" ++ python_native_changed_files="$(mktemp)" ++ changed_files_for_coverage >"$python_native_changed_files" ++ chmod 0444 "$python_native_changed_files" ++ ++ append "### ${label}" ++ append "" ++ append '```text' ++ append_command "$@" ++ set +e ++ timeout --kill-after=20 900 setpriv \ ++ --reuid "$OPENCODE_SANDBOX_UID" \ ++ --regid "$OPENCODE_SANDBOX_GID" \ ++ --clear-groups \ ++ env \ ++ -u ACTIONS_ID_TOKEN_REQUEST_TOKEN \ ++ -u ACTIONS_ID_TOKEN_REQUEST_URL \ ++ -u ACTIONS_RUNTIME_TOKEN \ ++ -u GH_TOKEN \ ++ -u GITHUB_TOKEN \ ++ GITHUB_ENV=/dev/null \ ++ GITHUB_PATH=/dev/null \ ++ GITHUB_OUTPUT=/dev/null \ ++ GITHUB_STEP_SUMMARY=/dev/null \ ++ BASH_ENV=/dev/null \ ++ UV_NO_BUILD=1 \ ++ GIT_CONFIG_NOSYSTEM=1 \ ++ GIT_CONFIG_GLOBAL=/dev/null \ ++ GIT_CONFIG_COUNT=1 \ ++ GIT_CONFIG_KEY_0=safe.directory \ ++ GIT_CONFIG_VALUE_0=/work \ ++ HOME=/work/.opencode-sandbox-home \ ++ XDG_CACHE_HOME=/work/.opencode-sandbox-cache \ ++ CARGO_HOME=/work/.opencode-sandbox-home/.cargo \ ++ PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \ ++ "$@" >"$python_native_pytest_log" 2>&1 ++ rc=$? ++ set -e ++ emit_captured_log "$python_native_pytest_log" ++ append '```' ++ append "" ++ ++ if [ "$rc" -eq 0 ]; then ++ append "- Result: PASS" ++ else ++ if run_python_native_extension_classifier \ ++ "$project_dir" \ ++ "$python_native_pytest_log" \ ++ "$python_native_changed_files" >/dev/null 2>&1; then ++ append "### Python native-extension source-only deferral" ++ append "" ++ append "- Result: DEFERRED" ++ append "- Reason: the unchanged declared PyO3 module was unavailable in the source-only sandbox; exact-head Python, Rust/PyO3, and package CheckRuns must all complete successfully before approval." ++ append "" ++ python_native_peer_check_required=1 ++ else ++ append "- Result: FAIL (exit ${rc})" ++ failures=$((failures + 1)) ++ fi ++ fi ++ append "" ++ rm -f "$python_native_pytest_log" "$python_native_changed_files" ++ } ++ + run_r_package_testthat() { + local package_name="$1" + local log_file rc classification description_snapshot +@@ -1112,14 +1200,16 @@ jobs: + if [ -n "$configured_commands_json" ]; then + while IFS= read -r configured_command_json; do + [ -n "$configured_command_json" ] || continue +- run_and_capture "Python configured CI test suite (${project_dir})" \ ++ run_python_test_and_capture \ ++ "Python configured CI test suite (${project_dir})" "$project_dir" \ + python3 "${GITHUB_WORKSPACE}/scripts/ci/safe_pytest_command.py" execute \ + --project-dir "$project_dir" \ + --command-json "$configured_command_json" + done <<<"$configured_commands_json" + else +- run_and_capture "Python coverage with missing-line report (${project_dir})" \ ++ run_python_test_and_capture \ ++ "Python coverage with missing-line report (${project_dir})" "$project_dir" \ + bash -c 'cd "$1" && PYTHONPATH="$([ -d src ] && printf src:. || printf .)" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing' bash "$project_dir" + fi + done < <(tracked_python_projects_with_tests) +@@ -1994,6 +2084,9 @@ jobs: + if [ "$r_peer_check_required" -eq 1 ]; then + append "- R test evidence: deferred package-load failures require a successful current-head peer R CMD check" + fi ++ if [ "$python_native_peer_check_required" -eq 1 ]; then ++ append "- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks" ++ fi + fi + else + append "- Result: FAIL" +@@ -7092,6 +7185,92 @@ jobs: + return 2 + } + ++ coverage_defers_to_python_native_peer_checks() { ++ printf '%s\n' "${COVERAGE_EVIDENCE_SUMMARY:-}" | ++ grep -Fq -- "- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks" ++ } ++ ++ collect_successful_python_native_peer_check_evidence() { ++ local output_file="$1" ++ local owner="${GH_REPOSITORY%%/*}" ++ local name="${GH_REPOSITORY#*/}" ++ local graphql_file ++ graphql_file="$(mktemp)" ++ ++ # Materialize trusted current-head GraphQL check-runs as the helper's ++ # bounded JSON contract. A stale PR head yields an empty list. ++ if ! timeout "$(check_lookup_api_timeout_seconds)s" gh api graphql \ ++ -f owner="$owner" \ ++ -f name="$name" \ ++ -F number="$PR_NUMBER" \ ++ -f query=' ++ query($owner:String!,$name:String!,$number:Int!) { ++ repository(owner:$owner,name:$name) { ++ pullRequest(number:$number) { ++ headRefOid ++ statusCheckRollup { ++ contexts(first: 100) { ++ nodes { ++ __typename ++ ... on CheckRun { ++ name ++ status ++ conclusion ++ checkSuite { ++ workflowRun { ++ workflow { ++ name ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ ' >"$graphql_file"; then ++ rm -f "$graphql_file" ++ return 1 ++ fi ++ ++ jq --arg head_sha "$PR_HEAD_SHA" ' ++ .data.repository.pullRequest as $pr ++ | if (($pr.headRefOid // "") != $head_sha) then ++ [] ++ else ++ [ ++ ($pr.statusCheckRollup.contexts.nodes // [])[] ++ | select(.__typename == "CheckRun") ++ | { ++ __typename: "CheckRun", ++ workflow: (.checkSuite.workflowRun.workflow.name // ""), ++ name: (.name // ""), ++ head_sha: $head_sha, ++ status: (.status // ""), ++ conclusion: (.conclusion // "") ++ } ++ ] ++ end ++ ' "$graphql_file" >"$output_file" ++ rm -f "$graphql_file" ++ ++ python3 "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ ++ require-checks \ ++ --checks-json "$output_file" \ ++ --head-sha "$PR_HEAD_SHA" \ ++ --required-check "CI::python" \ ++ --required-check "CI::rust" \ ++ --required-check "CI::package" >/dev/null ++ } ++ ++ require_python_native_peer_checks_for_deferred_coverage() { ++ local checks_file ++ if ! coverage_defers_to_python_native_peer_checks; then ++ return 0 ++ fi ++ checks_file="$(mktemp)" ++ if collect_github_checks_with_retry \ ++ collect_successful_python_native_peer_check_evidence "$checks_file"; then ++ rm -f "$checks_file" ++ printf 'Verified successful exact-head Python, Rust/PyO3, and package CheckRuns after bounded source-only native-extension deferral.\n' ++ return 0 ++ fi ++ rm -f "$checks_file" ++ printf '::notice::Python native-extension source-only deferral cannot authorize approval without successful exact-head Python, Rust/PyO3, and package CheckRuns.\n' ++ return 1 ++ } ++ + coverage_defers_to_r_cmd_check() { + printf '%s\n' "${COVERAGE_EVIDENCE_SUMMARY:-}" | + grep -Fq -- "- R test evidence: deferred package-load failures require a successful current-head peer R CMD check" +@@ -7268,6 +7447,10 @@ jobs: + return 0 + fi + ++ if ! require_python_native_peer_checks_for_deferred_coverage; then ++ return 1 ++ fi ++ + if ! require_r_cmd_check_for_deferred_coverage; then + return 1 + fi +@@ -7470,6 +7653,13 @@ jobs: + if [ "${COVERAGE_EVIDENCE_RESULT:-skipped}" != "success" ]; then + request_changes_for_coverage_evidence_failure + fi ++ ++ if ! require_python_native_peer_checks_for_deferred_coverage; then ++ printf '::error::Python native-extension source-only coverage deferral is missing successful exact-head Python, Rust/PyO3, or package CheckRun evidence for head %s.\n' "$PR_HEAD_SHA" ++ echo "::endgroup::" ++ exit 1 ++ fi + + opencode_review_outcome="${OPENCODE_MODEL_POOL_OUTCOME:-unknown}" + printf 'OpenCode model-pool outcome=%s model=%s; publish stage performs no duplicate model-catalog pass.\n' \ From 247ebc6e89b6948b1848794cb52901dbac5e3c66 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 14:32:30 +0900 Subject: [PATCH 20/20] chore(coverage): remove staged PyO3 integration patch artifact --- ...hon-native-peer-workflow-integration.patch | 260 ------------------ 1 file changed, 260 deletions(-) delete mode 100644 docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch diff --git a/docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch b/docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch deleted file mode 100644 index 1146e731e..000000000 --- a/docs/superpowers/patches/2026-08-07-python-native-peer-workflow-integration.patch +++ /dev/null @@ -1,260 +0,0 @@ -diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml ---- a/.github/workflows/opencode-review-dispatch.yml -+++ b/.github/workflows/opencode-review-dispatch.yml -@@ -824,6 +824,7 @@ jobs: - summary_output_file="${RUNNER_TEMP}/coverage-evidence-output.md" - failures=0 - r_peer_check_required=0 -+ python_native_peer_check_required=0 - - append() { - printf '%s\n' "$*" >>"$summary_file" -@@ -904,6 +905,93 @@ jobs: - rm -f "$log_file" - } - -+ run_python_native_extension_classifier() { -+ local project_dir="$1" -+ local python_native_pytest_log="$2" -+ local python_native_changed_files="$3" -+ -+ [ -f "$project_dir/pyproject.toml" ] || return 1 -+ python3 -I "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ -+ classify-pytest \ -+ --log "$python_native_pytest_log" \ -+ --pyproject "$project_dir/pyproject.toml" \ -+ --changed-files "$python_native_changed_files" \ -+ --repo-root "$COVERAGE_SOURCE_WORKDIR" -+ } -+ -+ run_python_test_and_capture() { -+ local label="$1" -+ local project_dir="$2" -+ shift 2 -+ local python_native_pytest_log -+ local python_native_changed_files -+ local rc -+ -+ python_native_pytest_log="$(mktemp)" -+ python_native_changed_files="$(mktemp)" -+ changed_files_for_coverage >"$python_native_changed_files" -+ chmod 0444 "$python_native_changed_files" -+ -+ append "### ${label}" -+ append "" -+ append '```text' -+ append_command "$@" -+ set +e -+ timeout --kill-after=20 900 setpriv \ -+ --reuid "$OPENCODE_SANDBOX_UID" \ -+ --regid "$OPENCODE_SANDBOX_GID" \ -+ --clear-groups \ -+ env \ -+ -u ACTIONS_ID_TOKEN_REQUEST_TOKEN \ -+ -u ACTIONS_ID_TOKEN_REQUEST_URL \ -+ -u ACTIONS_RUNTIME_TOKEN \ -+ -u GH_TOKEN \ -+ -u GITHUB_TOKEN \ -+ GITHUB_ENV=/dev/null \ -+ GITHUB_PATH=/dev/null \ -+ GITHUB_OUTPUT=/dev/null \ -+ GITHUB_STEP_SUMMARY=/dev/null \ -+ BASH_ENV=/dev/null \ -+ UV_NO_BUILD=1 \ -+ GIT_CONFIG_NOSYSTEM=1 \ -+ GIT_CONFIG_GLOBAL=/dev/null \ -+ GIT_CONFIG_COUNT=1 \ -+ GIT_CONFIG_KEY_0=safe.directory \ -+ GIT_CONFIG_VALUE_0=/work \ -+ HOME=/work/.opencode-sandbox-home \ -+ XDG_CACHE_HOME=/work/.opencode-sandbox-cache \ -+ CARGO_HOME=/work/.opencode-sandbox-home/.cargo \ -+ PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \ -+ "$@" >"$python_native_pytest_log" 2>&1 -+ rc=$? -+ set -e -+ emit_captured_log "$python_native_pytest_log" -+ append '```' -+ append "" -+ -+ if [ "$rc" -eq 0 ]; then -+ append "- Result: PASS" -+ else -+ if run_python_native_extension_classifier \ -+ "$project_dir" \ -+ "$python_native_pytest_log" \ -+ "$python_native_changed_files" >/dev/null 2>&1; then -+ append "### Python native-extension source-only deferral" -+ append "" -+ append "- Result: DEFERRED" -+ append "- Reason: the unchanged declared PyO3 module was unavailable in the source-only sandbox; exact-head Python, Rust/PyO3, and package CheckRuns must all complete successfully before approval." -+ append "" -+ python_native_peer_check_required=1 -+ else -+ append "- Result: FAIL (exit ${rc})" -+ failures=$((failures + 1)) -+ fi -+ fi -+ append "" -+ rm -f "$python_native_pytest_log" "$python_native_changed_files" -+ } -+ - run_r_package_testthat() { - local package_name="$1" - local log_file rc classification description_snapshot -@@ -1112,14 +1200,16 @@ jobs: - if [ -n "$configured_commands_json" ]; then - while IFS= read -r configured_command_json; do - [ -n "$configured_command_json" ] || continue -- run_and_capture "Python configured CI test suite (${project_dir})" \ -+ run_python_test_and_capture \ -+ "Python configured CI test suite (${project_dir})" "$project_dir" \ - python3 "${GITHUB_WORKSPACE}/scripts/ci/safe_pytest_command.py" execute \ - --project-dir "$project_dir" \ - --command-json "$configured_command_json" - done <<<"$configured_commands_json" - else -- run_and_capture "Python coverage with missing-line report (${project_dir})" \ -+ run_python_test_and_capture \ -+ "Python coverage with missing-line report (${project_dir})" "$project_dir" \ - bash -c 'cd "$1" && PYTHONPATH="$([ -d src ] && printf src:. || printf .)" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing' bash "$project_dir" - fi - done < <(tracked_python_projects_with_tests) -@@ -1994,6 +2084,9 @@ jobs: - if [ "$r_peer_check_required" -eq 1 ]; then - append "- R test evidence: deferred package-load failures require a successful current-head peer R CMD check" - fi -+ if [ "$python_native_peer_check_required" -eq 1 ]; then -+ append "- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks" -+ fi - fi - else - append "- Result: FAIL" -@@ -7092,6 +7185,92 @@ jobs: - return 2 - } - -+ coverage_defers_to_python_native_peer_checks() { -+ printf '%s\n' "${COVERAGE_EVIDENCE_SUMMARY:-}" | -+ grep -Fq -- "- Python native-extension peer evidence: deferred source-only collection requires successful exact-head peer checks" -+ } -+ -+ collect_successful_python_native_peer_check_evidence() { -+ local output_file="$1" -+ local owner="${GH_REPOSITORY%%/*}" -+ local name="${GH_REPOSITORY#*/}" -+ local graphql_file -+ graphql_file="$(mktemp)" -+ -+ # Materialize trusted current-head GraphQL check-runs as the helper's -+ # bounded JSON contract. A stale PR head yields an empty list. -+ if ! timeout "$(check_lookup_api_timeout_seconds)s" gh api graphql \ -+ -f owner="$owner" \ -+ -f name="$name" \ -+ -F number="$PR_NUMBER" \ -+ -f query=' -+ query($owner:String!,$name:String!,$number:Int!) { -+ repository(owner:$owner,name:$name) { -+ pullRequest(number:$number) { -+ headRefOid -+ statusCheckRollup { -+ contexts(first: 100) { -+ nodes { -+ __typename -+ ... on CheckRun { -+ name -+ status -+ conclusion -+ checkSuite { -+ workflowRun { -+ workflow { -+ name -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ ' >"$graphql_file"; then -+ rm -f "$graphql_file" -+ return 1 -+ fi -+ -+ jq --arg head_sha "$PR_HEAD_SHA" ' -+ .data.repository.pullRequest as $pr -+ | if (($pr.headRefOid // "") != $head_sha) then -+ [] -+ else -+ [ -+ ($pr.statusCheckRollup.contexts.nodes // [])[] -+ | select(.__typename == "CheckRun") -+ | { -+ __typename: "CheckRun", -+ workflow: (.checkSuite.workflowRun.workflow.name // ""), -+ name: (.name // ""), -+ head_sha: $head_sha, -+ status: (.status // ""), -+ conclusion: (.conclusion // "") -+ } -+ ] -+ end -+ ' "$graphql_file" >"$output_file" -+ rm -f "$graphql_file" -+ -+ python3 "$GITHUB_WORKSPACE/scripts/ci/python_native_extension_peer_gate.py" \ -+ require-checks \ -+ --checks-json "$output_file" \ -+ --head-sha "$PR_HEAD_SHA" \ -+ --required-check "CI::python" \ -+ --required-check "CI::rust" \ -+ --required-check "CI::package" >/dev/null -+ } -+ -+ require_python_native_peer_checks_for_deferred_coverage() { -+ local checks_file -+ if ! coverage_defers_to_python_native_peer_checks; then -+ return 0 -+ fi -+ checks_file="$(mktemp)" -+ if collect_github_checks_with_retry \ -+ collect_successful_python_native_peer_check_evidence "$checks_file"; then -+ rm -f "$checks_file" -+ printf 'Verified successful exact-head Python, Rust/PyO3, and package CheckRuns after bounded source-only native-extension deferral.\n' -+ return 0 -+ fi -+ rm -f "$checks_file" -+ printf '::notice::Python native-extension source-only deferral cannot authorize approval without successful exact-head Python, Rust/PyO3, and package CheckRuns.\n' -+ return 1 -+ } -+ - coverage_defers_to_r_cmd_check() { - printf '%s\n' "${COVERAGE_EVIDENCE_SUMMARY:-}" | - grep -Fq -- "- R test evidence: deferred package-load failures require a successful current-head peer R CMD check" -@@ -7268,6 +7447,10 @@ jobs: - return 0 - fi - -+ if ! require_python_native_peer_checks_for_deferred_coverage; then -+ return 1 -+ fi -+ - if ! require_r_cmd_check_for_deferred_coverage; then - return 1 - fi -@@ -7470,6 +7653,13 @@ jobs: - if [ "${COVERAGE_EVIDENCE_RESULT:-skipped}" != "success" ]; then - request_changes_for_coverage_evidence_failure - fi -+ -+ if ! require_python_native_peer_checks_for_deferred_coverage; then -+ printf '::error::Python native-extension source-only coverage deferral is missing successful exact-head Python, Rust/PyO3, or package CheckRun evidence for head %s.\n' "$PR_HEAD_SHA" -+ echo "::endgroup::" -+ exit 1 -+ fi - - opencode_review_outcome="${OPENCODE_MODEL_POOL_OUTCOME:-unknown}" - printf 'OpenCode model-pool outcome=%s model=%s; publish stage performs no duplicate model-catalog pass.\n' \