Files
Open_Ultra_SAST/benchmarks/valid/readiness_contract.py
T

247 lines
8.9 KiB
Python

"""Benchmark-owned adapter for detection-core v2 readiness records.
Core owns immutable contracts and pure validation. Benchmark workflows retain
sequencing, persistence, and population work; this module only constructs the
canonical values and enforces the readiness gate before that work starts.
"""
from __future__ import annotations
import hashlib
from dataclasses import dataclass
from pathlib import Path
from openultrasast.core.canonical import JsonValue, content_digest
from openultrasast.core.coverage import InputManifest, InputReadReceipt, derive_input_manifest_digest, validate_instrument
from openultrasast.core.readiness import (
AnalysisIdentity,
FixtureResult,
ReadinessEvidenceUse,
ReadinessResult,
ReadinessStage,
ReadinessValidationError,
StageAuthorization,
StageResult,
authorize_stage,
derive_readiness_result_digest,
required_evidence_use,
validate_readiness_result,
)
class ReadinessBlocked(RuntimeError):
"""Population work is not authorized by current readiness evidence."""
def __init__(
self,
failing_stage: ReadinessStage | None,
reasons: tuple[str, ...],
correction_lever: str | None,
) -> None:
self.failing_stage = failing_stage
self.reasons = reasons
self.correction_lever = correction_lever
layer = "readiness contract" if failing_stage is None else failing_stage.value
correction = "" if correction_lever is None else f"; correction: {correction_lever}"
super().__init__(f"{layer}: {'; '.join(reasons)}{correction}")
def _read_input(input_id: str, path: Path) -> InputReadReceipt:
try:
payload = path.read_bytes()
except OSError:
return InputReadReceipt(input_id, str(path), False, 0, None)
return InputReadReceipt(input_id, str(path), True, len(payload), hashlib.sha256(payload).hexdigest())
def read_tracked_inputs(
inputs: tuple[tuple[str, Path], ...],
) -> tuple[InputManifest, tuple[InputReadReceipt, ...]]:
"""Open tracked inputs and emit receipts; validation later refuses bad reads."""
input_ids = tuple(input_id for input_id, _path in inputs)
manifest = InputManifest(input_ids, derive_input_manifest_digest(input_ids))
receipts = tuple(_read_input(input_id, path) for input_id, path in inputs)
return manifest, receipts
def _read_receipt_value(receipt: InputReadReceipt) -> JsonValue:
return {
"input_id": receipt.input_id,
"path": receipt.path,
"opened": receipt.opened,
"byte_count": receipt.byte_count,
"content_digest": receipt.content_digest,
}
def _instrument_value(manifest: InputManifest, receipts: tuple[InputReadReceipt, ...]) -> JsonValue:
return {
"tracked_input_ids": manifest.tracked_input_ids,
"manifest_digest": manifest.manifest_digest,
"receipts": tuple(_read_receipt_value(item) for item in receipts),
}
def derive_development_measurement_digest(
manifest: InputManifest,
receipts: tuple[InputReadReceipt, ...],
) -> str:
"""Bind a development measurement to the exact verified instrument reads."""
validate_instrument(manifest, receipts)
return content_digest(_instrument_value(manifest, receipts))
def build_readiness_result(
*,
arm_id: str,
family_id: str,
ecosystem: str,
stage: ReadinessStage,
result: StageResult,
identity: AnalysisIdentity,
predecessor_digests: tuple[str, ...],
fixture_results: tuple[FixtureResult, ...],
measurement_digest: str,
correction_lever: str | None,
population_digest: str | None,
population_freeze_digest: str | None,
population_use_receipt_digests: tuple[str, ...],
) -> ReadinessResult:
evidence_use = required_evidence_use(stage)
digest = derive_readiness_result_digest(
arm_id=arm_id,
family_id=family_id,
ecosystem=ecosystem,
stage=stage,
evidence_use=evidence_use,
population_digest=population_digest,
population_freeze_digest=population_freeze_digest,
population_use_receipt_digests=population_use_receipt_digests,
result=result,
identity=identity,
predecessor_digests=predecessor_digests,
fixture_results=fixture_results,
measurement_digest=measurement_digest,
correction_lever=correction_lever,
)
return ReadinessResult(
arm_id,
family_id,
ecosystem,
stage,
evidence_use,
population_digest,
population_freeze_digest,
population_use_receipt_digests,
result,
identity,
predecessor_digests,
fixture_results,
measurement_digest,
correction_lever,
digest,
)
@dataclass(frozen=True)
class DevelopmentObservation:
"""A v2 development result plus the instrument evidence that produced it."""
readiness_result: ReadinessResult
input_manifest: InputManifest
input_read_receipts: tuple[InputReadReceipt, ...]
observation_digest: str
def __post_init__(self) -> None:
validate_development_observation(self)
def _observation_value(value: DevelopmentObservation) -> JsonValue:
return {
"readiness_result_digest": value.readiness_result.result_digest,
"instrument": _instrument_value(value.input_manifest, value.input_read_receipts),
}
def validate_development_observation(value: DevelopmentObservation) -> None:
"""Revalidate the readiness result, instrument, and their exact binding."""
validate_readiness_result(value.readiness_result)
if value.readiness_result.stage is not ReadinessStage.DEVELOPMENT:
raise ReadinessValidationError("development observation must use the development stage")
if value.readiness_result.evidence_use is not ReadinessEvidenceUse.DEVELOPMENT_POPULATION:
raise ReadinessValidationError("development observation must use development population evidence")
expected_measurement = derive_development_measurement_digest(value.input_manifest, value.input_read_receipts)
if value.readiness_result.measurement_digest != expected_measurement:
raise ReadinessValidationError("development measurement does not match instrument receipts")
if value.observation_digest != content_digest(_observation_value(value)):
raise ReadinessValidationError("development observation digest does not match content")
def build_development_observation(
*,
arm_id: str,
family_id: str,
ecosystem: str,
identity: AnalysisIdentity,
population_digest: str,
population_freeze_digest: str,
population_use_receipt_digests: tuple[str, ...],
predecessor_digests: tuple[str, ...],
fixture_results: tuple[FixtureResult, ...],
input_manifest: InputManifest,
input_read_receipts: tuple[InputReadReceipt, ...],
result: StageResult = StageResult.PASSED,
correction_lever: str | None = None,
) -> DevelopmentObservation:
"""Create a development observation only from verified nonzero input reads."""
measurement = derive_development_measurement_digest(input_manifest, input_read_receipts)
readiness_result = build_readiness_result(
arm_id=arm_id,
family_id=family_id,
ecosystem=ecosystem,
stage=ReadinessStage.DEVELOPMENT,
result=result,
identity=identity,
predecessor_digests=predecessor_digests,
fixture_results=fixture_results,
measurement_digest=measurement,
correction_lever=correction_lever,
population_digest=population_digest,
population_freeze_digest=population_freeze_digest,
population_use_receipt_digests=population_use_receipt_digests,
)
probe = object.__new__(DevelopmentObservation)
object.__setattr__(probe, "readiness_result", readiness_result)
object.__setattr__(probe, "input_manifest", input_manifest)
object.__setattr__(probe, "input_read_receipts", input_read_receipts)
object.__setattr__(probe, "observation_digest", "")
digest = content_digest(_observation_value(probe))
return DevelopmentObservation(readiness_result, input_manifest, input_read_receipts, digest)
def require_stage_authorized(
target: ReadinessStage,
receipts: tuple[ReadinessResult, ...],
current_identity: AnalysisIdentity,
) -> StageAuthorization:
"""Refuse population work on missing, failed, stale, or malformed readiness."""
try:
authorization = authorize_stage(target, receipts, current_identity)
except ReadinessValidationError as exc:
raise ReadinessBlocked(None, (str(exc),), None) from exc
if not authorization.authorized:
raise ReadinessBlocked(authorization.failing_stage, authorization.reasons, authorization.correction_lever)
return authorization
__all__ = [
"DevelopmentObservation",
"ReadinessBlocked",
"build_development_observation",
"build_readiness_result",
"derive_development_measurement_digest",
"read_tracked_inputs",
"require_stage_authorized",
"validate_development_observation",
]