mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-17 16:37:30 +02:00
test: enforce gate 2 coverage and vertical contracts
This commit is contained in:
@@ -5,12 +5,21 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REQUIRED_GATE_FIELDS = {
|
||||
"id", "job", "marker", "runner", "prerequisites", "expected_cost", "coverage_paths"
|
||||
}
|
||||
SOFTWARE_ONLY_GATES = (
|
||||
"model-download-runtime",
|
||||
"external-evaluation",
|
||||
"network-services",
|
||||
"operator-ui",
|
||||
)
|
||||
SHA = re.compile(r"^[0-9a-f]{40}$")
|
||||
ISSUE_URL = "https://github.com/elder-plinius/OBLITERATUS/issues/"
|
||||
|
||||
|
||||
def validate(policy_path: Path, quality_path: Path, workflow_path: Path) -> list[str]:
|
||||
@@ -67,6 +76,86 @@ def validate(policy_path: Path, quality_path: Path, workflow_path: Path) -> list
|
||||
return errors
|
||||
|
||||
|
||||
def _load_json_object(path: Path, label: str, errors: list[str]) -> dict:
|
||||
try:
|
||||
value = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
errors.append(f"cannot read {label}: {exc}")
|
||||
return {}
|
||||
if not isinstance(value, dict):
|
||||
errors.append(f"{label} root must be an object")
|
||||
return {}
|
||||
return value
|
||||
|
||||
|
||||
def _valid_stale_exception(reason: str | None, issue: str | None) -> bool:
|
||||
return (
|
||||
isinstance(reason, str)
|
||||
and bool(reason.strip())
|
||||
and isinstance(issue, str)
|
||||
and issue.startswith(ISSUE_URL)
|
||||
)
|
||||
|
||||
|
||||
def validate_evidence(
|
||||
policy_path: Path,
|
||||
evidence_dir: Path,
|
||||
*,
|
||||
candidate_sha: str,
|
||||
required_gates: list[str] | None = None,
|
||||
stale_exception_reason: str | None = None,
|
||||
stale_exception_issue: str | None = None,
|
||||
) -> list[str]:
|
||||
"""Validate selected software-only conditional evidence against a candidate SHA."""
|
||||
|
||||
errors: list[str] = []
|
||||
policy = _load_json_object(policy_path, "conditional policy", errors)
|
||||
if errors:
|
||||
return errors
|
||||
|
||||
if SHA.fullmatch(candidate_sha) is None:
|
||||
errors.append("candidate SHA must be a 40-character lowercase hex commit")
|
||||
|
||||
gates = policy.get("gates")
|
||||
policy_gate_ids = {
|
||||
gate.get("id")
|
||||
for gate in gates
|
||||
if isinstance(gates, list) and isinstance(gate, dict)
|
||||
} if isinstance(gates, list) else set()
|
||||
requested = required_gates or list(SOFTWARE_ONLY_GATES)
|
||||
for gate_id in requested:
|
||||
if gate_id not in SOFTWARE_ONLY_GATES:
|
||||
errors.append(f"hardware or credential gate is not software-only: {gate_id}")
|
||||
if gate_id not in policy_gate_ids:
|
||||
errors.append(f"conditional policy does not define gate {gate_id}")
|
||||
|
||||
exception = _valid_stale_exception(stale_exception_reason, stale_exception_issue)
|
||||
if (stale_exception_reason or stale_exception_issue) and not exception:
|
||||
errors.append(
|
||||
"stale evidence exception requires a non-empty reason and an OBLITERATUS issue URL",
|
||||
)
|
||||
|
||||
for gate_id in requested:
|
||||
evidence = _load_json_object(
|
||||
evidence_dir / f"{gate_id}.json",
|
||||
f"conditional evidence {gate_id}",
|
||||
errors,
|
||||
)
|
||||
if not evidence:
|
||||
continue
|
||||
if evidence.get("gate") != gate_id:
|
||||
errors.append(f"conditional evidence {gate_id} records gate {evidence.get('gate')!r}")
|
||||
if evidence.get("status") != "passed":
|
||||
errors.append(f"conditional evidence {gate_id} did not pass: {evidence.get('status')!r}")
|
||||
evidence_sha = evidence.get("git_sha")
|
||||
if evidence_sha != candidate_sha and not exception:
|
||||
errors.append(
|
||||
f"conditional evidence {gate_id} git_sha {evidence_sha!r} "
|
||||
f"does not match candidate {candidate_sha}",
|
||||
)
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--policy", type=Path, default=Path("ci/conditional-test-policy.json"))
|
||||
@@ -74,8 +163,47 @@ def main() -> int:
|
||||
parser.add_argument(
|
||||
"--workflow", type=Path, default=Path(".github/workflows/conditional-tests.yml")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--evidence-dir",
|
||||
type=Path,
|
||||
help="validate software-only conditional evidence files in this directory",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--candidate-sha",
|
||||
help="40-character candidate commit SHA required for evidence freshness validation",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--require-gate",
|
||||
action="append",
|
||||
default=[],
|
||||
help="software-only gate that must have current passed evidence (repeatable)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--stale-evidence-reason",
|
||||
default="",
|
||||
help="maintainer reason for accepting older software conditional evidence",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--stale-evidence-issue",
|
||||
default="",
|
||||
help="OBLITERATUS issue URL approving older software conditional evidence",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
errors = validate(args.policy, args.quality, args.workflow)
|
||||
if args.evidence_dir is not None:
|
||||
if not args.candidate_sha:
|
||||
errors.append("evidence freshness validation requires --candidate-sha")
|
||||
else:
|
||||
errors.extend(
|
||||
validate_evidence(
|
||||
args.policy,
|
||||
args.evidence_dir,
|
||||
candidate_sha=args.candidate_sha,
|
||||
required_gates=args.require_gate or None,
|
||||
stale_exception_reason=args.stale_evidence_reason or None,
|
||||
stale_exception_issue=args.stale_evidence_issue or None,
|
||||
),
|
||||
)
|
||||
if errors:
|
||||
for error in errors:
|
||||
print(f"ERROR: {error}")
|
||||
|
||||
@@ -13,11 +13,11 @@ from typing import Any
|
||||
|
||||
|
||||
BASELINE_FLOORS = {
|
||||
"repository_statement": 70.0,
|
||||
"repository_branch": 55.0,
|
||||
"repository_statement": 75.0,
|
||||
"repository_branch": 60.0,
|
||||
"changed_line": 90.0,
|
||||
"mature_cpu_statement": 90.0,
|
||||
"mature_cpu_branch": 78.0,
|
||||
"mature_cpu_statement": 92.0,
|
||||
"mature_cpu_branch": 80.0,
|
||||
"mutation_score": 75.0,
|
||||
"warning_budget": 0.0,
|
||||
}
|
||||
|
||||
@@ -15,18 +15,26 @@ from xml.etree import ElementTree
|
||||
|
||||
|
||||
DEFAULT_TESTS = (
|
||||
"tests/test_bayesian_optimizer_contracts.py",
|
||||
"tests/test_config.py",
|
||||
"tests/test_config_properties.py",
|
||||
"tests/test_conditional_evidence_freshness.py",
|
||||
"tests/test_coverage_thresholds.py",
|
||||
"tests/test_evaluation_reporting_contracts.py",
|
||||
"tests/test_lm_eval_reporting_contracts.py",
|
||||
"tests/test_informed_pipeline_contracts.py",
|
||||
"tests/test_model_profile_contracts.py",
|
||||
"tests/test_numerical_contracts.py",
|
||||
"tests/test_package_export_contracts.py",
|
||||
"tests/test_persistence_contracts.py",
|
||||
"tests/test_property_contracts.py",
|
||||
"tests/test_advanced_metrics.py",
|
||||
"tests/test_metrics.py",
|
||||
"tests/test_remote_contracts.py",
|
||||
"tests/test_remaining_cpu_contracts.py",
|
||||
"tests/test_runtime_contracts.py",
|
||||
"tests/test_strategy_navigation_contracts.py",
|
||||
"tests/test_sweep_contracts.py",
|
||||
"tests/test_telemetry_failure_contracts.py",
|
||||
)
|
||||
HASH_SEEDS = ("0", "1", "8675309")
|
||||
|
||||
Reference in New Issue
Block a user