feat(detection-core): add complete measurement records

This commit is contained in:
Marius Ciepluch
2026-10-31 18:45:19 +01:00
parent 9d266b2f11
commit a3cea1096c
4 changed files with 913 additions and 1 deletions
+1 -1
View File
@@ -228,7 +228,7 @@
- _Requirements: 11.6, 15.1, 15.3_ - _Requirements: 11.6, 15.1, 15.3_
- _Boundary: Measurement_ - _Boundary: Measurement_
- _Depends: 4.1, 4.4_ - _Depends: 4.1, 4.4_
- [ ] 8.2 Build complete measurement records and typed repository-holdout reports - [x] 8.2 Build complete measurement records and typed repository-holdout reports
- Record family decision vocabulary, neutral outcomes, evidence bases, fixed metrics, semantic funnel, model outcomes/failures, instrument failures, runtime, and readiness ancestry. - Record family decision vocabulary, neutral outcomes, evidence bases, fixed metrics, semantic funnel, model outcomes/failures, instrument failures, runtime, and readiness ancestry.
- Require every later-stage record to carry current prerequisite readiness stage results and digests. - Require every later-stage record to carry current prerequisite readiness stage results and digests.
- Define a typed repository-holdout report with an explicit evidence-use role, training and holdout manifest references, repository identities, fitting lineage, and distinct measurement/report identities. - Define a typed repository-holdout report with an explicit evidence-use role, training and holdout manifest references, repository identities, fitting lineage, and distinct measurement/report identities.
@@ -0,0 +1,88 @@
"""Canonical constructors for complete measurement aggregates."""
from __future__ import annotations
from openultrasast.core import canonical
from openultrasast.core.coverage import CoverageFunnel, ReasonCount
from openultrasast.core.measurement import BaselineIdentity, MetricResult
from openultrasast.core.measurement_record import (
BasisCount,
MeasurementRecordV2,
OutcomeCount,
ReadinessReference,
RepositoryHoldoutReport,
_record_value,
_report_value,
_validate_record_fields,
_validate_report_fields,
)
from openultrasast.core.readiness import ReadinessEvidenceUse
def create_measurement_record(
*,
identity: BaselineIdentity,
family_decision_vocabulary: tuple[str, ...],
family_decision_counts: tuple[ReasonCount, ...],
neutral_outcome_counts: tuple[OutcomeCount, ...],
evidence_basis_counts: tuple[BasisCount, ...],
metrics: tuple[MetricResult, ...],
stage_funnel: CoverageFunnel,
instrument_failures: tuple[str, ...],
model_outcome_counts: tuple[ReasonCount, ...],
model_failures: tuple[str, ...],
runtime_ms: int,
prerequisite_readiness: tuple[ReadinessReference, ...],
) -> MeasurementRecordV2:
values = (
identity,
family_decision_vocabulary,
family_decision_counts,
neutral_outcome_counts,
evidence_basis_counts,
metrics,
stage_funnel,
instrument_failures,
model_outcome_counts,
model_failures,
runtime_ms,
prerequisite_readiness,
)
draft = object.__new__(MeasurementRecordV2)
for field, item in zip(MeasurementRecordV2.__dataclass_fields__, (*values, "0" * 64), strict=True):
object.__setattr__(draft, field, item)
_validate_record_fields(draft)
digest = canonical.content_digest(_record_value(draft))
return MeasurementRecordV2(*values, digest)
def create_repository_holdout_report(
*,
component_digest: str,
role: ReadinessEvidenceUse,
training_population_manifest: canonical.ContentRef,
holdout_population_manifest: canonical.ContentRef,
training_repository_digests: tuple[str, ...],
holdout_repository_digests: tuple[str, ...],
fitting_lineage_manifest_digest: str,
measurement: MeasurementRecordV2,
) -> RepositoryHoldoutReport:
values = (
component_digest,
role,
training_population_manifest,
holdout_population_manifest,
training_repository_digests,
holdout_repository_digests,
fitting_lineage_manifest_digest,
measurement,
)
draft = object.__new__(RepositoryHoldoutReport)
for field, item in zip(RepositoryHoldoutReport.__dataclass_fields__, (*values, "0" * 64), strict=True):
object.__setattr__(draft, field, item)
_validate_report_fields(draft)
digest = canonical.content_digest(_report_value(draft))
return RepositoryHoldoutReport(*values, digest)
__all__ = ["create_measurement_record", "create_repository_holdout_report"]
@@ -0,0 +1,439 @@
"""Complete measurement records and repository-holdout reports."""
from __future__ import annotations
import re
from dataclasses import dataclass
from openultrasast.core import canonical
from openultrasast.core.coverage import CoverageFunnel, ReasonCount
from openultrasast.core.evidence import EvidenceBasis, NeutralOutcome
from openultrasast.core.measurement import (
BaselineIdentity,
MetricId,
MetricResult,
derive_baseline_identity_digest,
metric_result_value,
)
from openultrasast.core.readiness import ReadinessEvidenceUse, ReadinessStage, StageResult
MEASUREMENT_RECORD_SCHEMA = "openultrasast.detection-core.measurement-record.v2"
REPOSITORY_HOLDOUT_REPORT_SCHEMA = "openultrasast.detection-core.repository-holdout-report.v2"
_DIGEST = re.compile(r"[0-9a-f]{64}")
_PREREQUISITE_STAGES = tuple(ReadinessStage)[:5]
class MeasurementRecordError(ValueError):
"""A measurement or holdout report violates the fixed schema."""
def _digest(value: object, field: str) -> None:
if not isinstance(value, str) or _DIGEST.fullmatch(value) is None:
raise MeasurementRecordError(f"{field} must be a full lowercase SHA-256 digest")
def _count(value: object, field: str, *, positive: bool = False) -> int:
minimum = 1 if positive else 0
if isinstance(value, bool) or not isinstance(value, int) or value < minimum:
qualifier = "positive" if positive else "non-negative"
raise MeasurementRecordError(f"{field} must be a {qualifier} integer")
return value
def _text_tuple(value: object, field: str, *, allow_empty: bool = True) -> tuple[str, ...]:
if not isinstance(value, tuple) or (not value and not allow_empty):
qualifier = "a tuple" if allow_empty else "a nonempty tuple"
raise MeasurementRecordError(f"{field} must be {qualifier} of nonempty text")
if not all(isinstance(item, str) and item for item in value):
raise MeasurementRecordError(f"{field} must contain nonempty text")
if len(set(value)) != len(value):
raise MeasurementRecordError(f"{field} contains a duplicate")
return value
def _canonical_digest_tuple(value: object, field: str) -> tuple[str, ...]:
if not isinstance(value, tuple) or not value:
raise MeasurementRecordError(f"{field} must be a nonempty tuple")
for item in value:
_digest(item, field.removesuffix("s"))
if value != tuple(sorted(value)):
raise MeasurementRecordError(f"{field} must use canonical order")
if len(set(value)) != len(value):
raise MeasurementRecordError(f"{field} contains a duplicate")
return value
@dataclass(frozen=True)
class OutcomeCount:
outcome: NeutralOutcome
count: int
def __post_init__(self) -> None:
if not isinstance(self.outcome, NeutralOutcome):
raise MeasurementRecordError("outcome count must use NeutralOutcome")
_count(self.count, "outcome count")
@dataclass(frozen=True)
class BasisCount:
basis: EvidenceBasis
count: int
def __post_init__(self) -> None:
if not isinstance(self.basis, EvidenceBasis):
raise MeasurementRecordError("basis count must use EvidenceBasis")
_count(self.count, "basis count")
@dataclass(frozen=True)
class ReadinessReference:
stage: ReadinessStage
result: StageResult
result_digest: str
def __post_init__(self) -> None:
if not isinstance(self.stage, ReadinessStage):
raise MeasurementRecordError("readiness stage must use ReadinessStage")
if not isinstance(self.result, StageResult):
raise MeasurementRecordError("readiness result must use StageResult")
_digest(self.result_digest, "readiness digest")
@dataclass(frozen=True)
class MeasurementRecordV2:
identity: BaselineIdentity
family_decision_vocabulary: tuple[str, ...]
family_decision_counts: tuple[ReasonCount, ...]
neutral_outcome_counts: tuple[OutcomeCount, ...]
evidence_basis_counts: tuple[BasisCount, ...]
metrics: tuple[MetricResult, ...]
stage_funnel: CoverageFunnel
instrument_failures: tuple[str, ...]
model_outcome_counts: tuple[ReasonCount, ...]
model_failures: tuple[str, ...]
runtime_ms: int
prerequisite_readiness: tuple[ReadinessReference, ...]
record_digest: str
def __post_init__(self) -> None:
validate_measurement_record(self)
@dataclass(frozen=True)
class RepositoryHoldoutReport:
component_digest: str
role: ReadinessEvidenceUse
training_population_manifest: canonical.ContentRef
holdout_population_manifest: canonical.ContentRef
training_repository_digests: tuple[str, ...]
holdout_repository_digests: tuple[str, ...]
fitting_lineage_manifest_digest: str
measurement: MeasurementRecordV2
report_digest: str
def __post_init__(self) -> None:
validate_repository_holdout_report(self)
def _reason_count_value(value: ReasonCount) -> canonical.JsonValue:
return {"reason": value.reason, "count": value.count}
def _outcome_count_value(value: OutcomeCount) -> canonical.JsonValue:
return {"outcome": value.outcome.value, "count": value.count}
def _basis_count_value(value: BasisCount) -> canonical.JsonValue:
return {"basis": value.basis.value, "count": value.count}
def _readiness_value(value: ReadinessReference) -> canonical.JsonValue:
return {"stage": value.stage.value, "result": value.result.value, "result_digest": value.result_digest}
def _funnel_value(value: CoverageFunnel) -> canonical.JsonValue:
return {
"inputs_read": value.inputs_read,
"questions_formed": value.questions_formed,
"sites_considered": value.sites_considered,
"sites_recognized": value.sites_recognized,
"transitions_materialized": value.transitions_materialized,
"security_objects_constructed": value.security_objects_constructed,
"constraints_evaluated": value.constraints_evaluated,
"determinate_outcomes": value.determinate_outcomes,
"findings_emitted": value.findings_emitted,
"relation_status_counts": tuple({"status": item.status.value, "count": item.count} for item in value.relation_status_counts),
"unresolved_by_reason": tuple(_reason_count_value(item) for item in value.unresolved_by_reason),
}
def _content_ref_value(value: canonical.ContentRef) -> canonical.JsonValue:
return {
"artifact_id": value.artifact_id,
"content_digest": value.content_digest,
"media_type": value.media_type,
"byte_length": value.byte_length,
"retention_policy_digest": value.retention_policy_digest,
"storage_receipt_digest": value.storage_receipt_digest,
}
def _validate_named_counts(
values: object,
field: str,
*,
allowed: tuple[str, ...] | None = None,
canonical_order: bool = True,
) -> tuple[ReasonCount, ...]:
if not isinstance(values, tuple) or not all(isinstance(item, ReasonCount) for item in values):
raise MeasurementRecordError(f"{field} must be a tuple of ReasonCount values")
for item in values:
item.__post_init__()
names = tuple(item.reason for item in values)
if len(names) != len(set(names)):
raise MeasurementRecordError(f"{field} contains a duplicate")
if canonical_order and names != tuple(sorted(names)):
raise MeasurementRecordError(f"{field} must use canonical order")
if allowed is not None and any(name not in allowed for name in names):
raise MeasurementRecordError("family decision counts must use the decision vocabulary")
return values
def _validate_enum_counts(value: MeasurementRecordV2) -> None:
if not isinstance(value.neutral_outcome_counts, tuple) or tuple(
item.outcome for item in value.neutral_outcome_counts if isinstance(item, OutcomeCount)
) != tuple(NeutralOutcome):
raise MeasurementRecordError("outcome counts must contain every neutral outcome in canonical order")
if not isinstance(value.evidence_basis_counts, tuple) or tuple(
item.basis for item in value.evidence_basis_counts if isinstance(item, BasisCount)
) != tuple(EvidenceBasis):
raise MeasurementRecordError("basis counts must contain every evidence basis in canonical order")
for outcome_count in value.neutral_outcome_counts:
outcome_count.__post_init__()
for basis_count in value.evidence_basis_counts:
basis_count.__post_init__()
def _validate_metrics(value: MeasurementRecordV2) -> None:
if not isinstance(value.metrics, tuple) or not all(isinstance(item, MetricResult) for item in value.metrics):
raise MeasurementRecordError("metrics must be a tuple of MetricResult values")
if tuple(item.metric_id for item in value.metrics) != tuple(MetricId):
raise MeasurementRecordError("metrics must contain every standard metric in canonical order")
for item in value.metrics:
metric_result_value(item)
def _validate_readiness(value: MeasurementRecordV2) -> None:
references = value.prerequisite_readiness
if not isinstance(references, tuple) or not all(isinstance(item, ReadinessReference) for item in references):
raise MeasurementRecordError("prerequisite readiness must contain ReadinessReference values")
if tuple(item.stage for item in references) != _PREREQUISITE_STAGES:
raise MeasurementRecordError("prerequisite readiness must contain every earlier stage in canonical order")
for item in references:
item.__post_init__()
if len({item.result_digest for item in references}) != len(references):
raise MeasurementRecordError("prerequisite readiness contains a copied readiness digest")
def _validate_record_fields(value: MeasurementRecordV2) -> None:
if not isinstance(value.identity, BaselineIdentity):
raise MeasurementRecordError("identity must be a BaselineIdentity")
derive_baseline_identity_digest(value.identity)
vocabulary = _text_tuple(value.family_decision_vocabulary, "family decision vocabulary", allow_empty=False)
decisions = _validate_named_counts(
value.family_decision_counts,
"family decision counts",
allowed=vocabulary,
canonical_order=False,
)
if tuple(item.reason for item in decisions) != tuple(name for name in vocabulary if name in {i.reason for i in decisions}):
raise MeasurementRecordError("family decision counts must follow decision vocabulary order")
_validate_enum_counts(value)
_validate_metrics(value)
if not isinstance(value.stage_funnel, CoverageFunnel):
raise MeasurementRecordError("stage_funnel must be a CoverageFunnel")
value.stage_funnel.__post_init__()
_text_tuple(value.instrument_failures, "instrument_failures")
_validate_named_counts(value.model_outcome_counts, "model outcome counts")
_text_tuple(value.model_failures, "model_failures")
_count(value.runtime_ms, "runtime", positive=True)
_validate_readiness(value)
_validate_record_totals(value, decisions)
def _validate_record_totals(value: MeasurementRecordV2, decisions: tuple[ReasonCount, ...]) -> None:
question_count = value.stage_funnel.questions_formed
if sum(item.count for item in decisions) != question_count:
raise MeasurementRecordError("family decision counts must equal the question count")
if sum(item.count for item in value.neutral_outcome_counts) != question_count:
raise MeasurementRecordError("neutral outcome counts must equal the question count")
if sum(item.count for item in value.evidence_basis_counts) != question_count:
raise MeasurementRecordError("evidence basis counts must equal the question count")
determinate = sum(item.count for item in value.neutral_outcome_counts if item.outcome is not NeutralOutcome.INDETERMINATE)
if determinate != value.stage_funnel.determinate_outcomes:
raise MeasurementRecordError("determinate outcome counts must equal the funnel determinate count")
violation_count = next(item.count for item in value.neutral_outcome_counts if item.outcome is NeutralOutcome.VIOLATION)
if value.stage_funnel.findings_emitted > violation_count:
raise MeasurementRecordError("findings cannot exceed violation outcomes")
_validate_model_totals(value)
_validate_clean_measurement(value)
def _validate_model_totals(value: MeasurementRecordV2) -> None:
model_metric = next(item for item in value.metrics if item.metric_id is MetricId.MODEL_SUCCESS_RATE)
by_outcome = {item.reason: item.count for item in value.model_outcome_counts}
if not set(by_outcome) <= {"failure", "success"}:
raise MeasurementRecordError("model outcomes must use the closed success/failure vocabulary")
successful = by_outcome.get("success", 0)
failed = by_outcome.get("failure", 0)
if successful != model_metric.numerator:
raise MeasurementRecordError("successful model outcome count must equal the model-success numerator")
if failed != model_metric.denominator - model_metric.numerator:
raise MeasurementRecordError("model outcome counts for failure must equal model calls minus successes")
if successful + failed != model_metric.denominator:
raise MeasurementRecordError("model outcome counts must equal the model-call denominator")
if failed and not value.model_failures:
raise MeasurementRecordError("failed model calls require named model_failures")
if not failed and value.model_failures:
raise MeasurementRecordError("model_failures require a failed model outcome")
def _validate_clean_measurement(value: MeasurementRecordV2) -> None:
if not (_zero_measurement(value) or _clean_looking(value)):
return
if value.stage_funnel.inputs_read <= 0:
raise MeasurementRecordError("zero or clean-looking measurement requires a tracked input read")
if any(item.result is not StageResult.PASSED for item in value.prerequisite_readiness):
raise MeasurementRecordError("zero or clean-looking measurement requires passed readiness prerequisites")
def _zero_measurement(value: MeasurementRecordV2) -> bool:
funnel = value.stage_funnel
downstream = (
funnel.questions_formed,
funnel.sites_considered,
funnel.sites_recognized,
funnel.transitions_materialized,
funnel.security_objects_constructed,
funnel.constraints_evaluated,
funnel.determinate_outcomes,
funnel.findings_emitted,
)
return not any(downstream) and all(item.numerator == 0 for item in value.metrics)
def _clean_looking(value: MeasurementRecordV2) -> bool:
violation = next(item.count for item in value.neutral_outcome_counts if item.outcome is NeutralOutcome.VIOLATION)
return value.stage_funnel.findings_emitted == 0 and violation == 0 and not value.instrument_failures and not value.model_failures
def _record_value(value: MeasurementRecordV2) -> canonical.JsonValue:
return {
"schema_version": MEASUREMENT_RECORD_SCHEMA,
"baseline_identity_digest": derive_baseline_identity_digest(value.identity),
"family_decision_vocabulary": value.family_decision_vocabulary,
"family_decision_counts": tuple(_reason_count_value(item) for item in value.family_decision_counts),
"neutral_outcome_counts": tuple(_outcome_count_value(item) for item in value.neutral_outcome_counts),
"evidence_basis_counts": tuple(_basis_count_value(item) for item in value.evidence_basis_counts),
"metrics": tuple(metric_result_value(item) for item in value.metrics),
"stage_funnel": _funnel_value(value.stage_funnel),
"instrument_failures": value.instrument_failures,
"model_outcome_counts": tuple(_reason_count_value(item) for item in value.model_outcome_counts),
"model_failures": value.model_failures,
"runtime_ms": value.runtime_ms,
"prerequisite_readiness": tuple(_readiness_value(item) for item in value.prerequisite_readiness),
}
def validate_measurement_record(value: MeasurementRecordV2) -> None:
if not isinstance(value, MeasurementRecordV2):
raise MeasurementRecordError("measurement must be a MeasurementRecordV2")
try:
_validate_record_fields(value)
except MeasurementRecordError:
raise
except (TypeError, ValueError) as exc:
raise MeasurementRecordError(str(exc)) from exc
expected = canonical.content_digest(_record_value(value))
_digest(value.record_digest, "record digest")
if value.record_digest != expected:
raise MeasurementRecordError("record digest does not match measurement content")
def validate_repository_holdout_report(value: RepositoryHoldoutReport) -> None:
if not isinstance(value, RepositoryHoldoutReport):
raise MeasurementRecordError("report must be a RepositoryHoldoutReport")
_validate_report_fields(value)
if value.report_digest == value.measurement.record_digest:
raise MeasurementRecordError("report identity must be distinct from measurement identity")
expected = canonical.content_digest(_report_value(value))
_digest(value.report_digest, "report digest")
if value.report_digest != expected:
raise MeasurementRecordError("report digest does not match holdout content")
def _validate_report_fields(value: RepositoryHoldoutReport) -> None:
_digest(value.component_digest, "component digest")
if value.role is not ReadinessEvidenceUse.REPOSITORY_HOLDOUT:
raise MeasurementRecordError("holdout report role must be repository_holdout")
_validate_content_ref(value.training_population_manifest, "training population manifest")
_validate_content_ref(value.holdout_population_manifest, "holdout population manifest")
if value.training_population_manifest.content_digest == value.holdout_population_manifest.content_digest:
raise MeasurementRecordError("training and holdout manifests must be distinct")
training = _canonical_digest_tuple(value.training_repository_digests, "training repository digests")
holdout = _canonical_digest_tuple(value.holdout_repository_digests, "holdout repository digests")
if set(training) & set(holdout):
raise MeasurementRecordError("training and holdout repositories must be disjoint")
_digest(value.fitting_lineage_manifest_digest, "fitting lineage manifest digest")
validate_measurement_record(value.measurement)
if value.component_digest not in value.measurement.identity.model_program_digests:
raise MeasurementRecordError("holdout component must be an exact measured model component")
def _validate_content_ref(value: object, field: str) -> None:
if not isinstance(value, canonical.ContentRef):
raise MeasurementRecordError(f"{field} must be a ContentRef")
try:
value.__post_init__()
except (TypeError, ValueError) as exc:
raise MeasurementRecordError(str(exc)) from exc
if value.byte_length <= 0:
raise MeasurementRecordError(f"{field} must have nonzero bytes")
def _report_value(value: RepositoryHoldoutReport) -> canonical.JsonValue:
return {
"schema_version": REPOSITORY_HOLDOUT_REPORT_SCHEMA,
"component_digest": value.component_digest,
"role": value.role.value,
"training_population_manifest": _content_ref_value(value.training_population_manifest),
"holdout_population_manifest": _content_ref_value(value.holdout_population_manifest),
"training_repository_digests": value.training_repository_digests,
"holdout_repository_digests": value.holdout_repository_digests,
"fitting_lineage_manifest_digest": value.fitting_lineage_manifest_digest,
"measurement_record_digest": value.measurement.record_digest,
}
def measurement_record_value(value: MeasurementRecordV2) -> canonical.JsonValue:
validate_measurement_record(value)
return _record_value(value)
def repository_holdout_report_value(value: RepositoryHoldoutReport) -> canonical.JsonValue:
validate_repository_holdout_report(value)
return _report_value(value)
__all__ = [
"BasisCount",
"MeasurementRecordError",
"MeasurementRecordV2",
"OutcomeCount",
"ReadinessReference",
"RepositoryHoldoutReport",
"measurement_record_value",
"repository_holdout_report_value",
"validate_measurement_record",
"validate_repository_holdout_report",
]
+385
View File
@@ -0,0 +1,385 @@
"""Complete measurement record and repository-holdout contracts."""
from __future__ import annotations
from dataclasses import replace
import pytest
from test_core_measurement import _baseline, _scopes
from openultrasast.core import canonical
from openultrasast.core.coverage import CoverageFunnel, ReasonCount, RelationStatusCount
from openultrasast.core.evidence import EvidenceBasis, NeutralOutcome
from openultrasast.core.measurement import MetricId, evaluate_metric, standard_metric_rules
from openultrasast.core.measurement_factory import create_measurement_record, create_repository_holdout_report
from openultrasast.core.measurement_record import (
BasisCount,
MeasurementRecordError,
MeasurementRecordV2,
OutcomeCount,
ReadinessReference,
RepositoryHoldoutReport,
measurement_record_value,
repository_holdout_report_value,
validate_measurement_record,
validate_repository_holdout_report,
)
from openultrasast.core.readiness import ReadinessEvidenceUse, ReadinessStage, StageResult
from openultrasast.core.semantics import RelationStatus
def _digest(label: str) -> str:
return canonical.content_digest(label)
def _funnel(*, inputs_read: int = 1, findings: int = 1) -> CoverageFunnel:
return CoverageFunnel(
inputs_read=inputs_read,
questions_formed=2,
sites_considered=2,
sites_recognized=2,
transitions_materialized=2,
security_objects_constructed=2,
constraints_evaluated=2,
determinate_outcomes=2,
findings_emitted=findings,
relation_status_counts=(
RelationStatusCount(RelationStatus.COMPLETE, 2),
RelationStatusCount(RelationStatus.INCOMPLETE, 0),
RelationStatusCount(RelationStatus.UNRESOLVED, 0),
),
unresolved_by_reason=(),
)
def _readiness(*, result: StageResult = StageResult.PASSED) -> tuple[ReadinessReference, ...]:
return tuple(
ReadinessReference(stage, result, _digest(f"readiness:{stage.value}:{result.value}")) for stage in tuple(ReadinessStage)[:5]
)
def _metrics(*, model_numerator: int = 1, model_denominator: int = 2) -> tuple[object, ...]:
return tuple(
evaluate_metric(
rule,
numerator=model_numerator if rule.metric_id is MetricId.MODEL_SUCCESS_RATE else 1,
denominator=model_denominator if rule.metric_id is MetricId.MODEL_SUCCESS_RATE else 2,
)
for rule in standard_metric_rules(_scopes())
)
def _record(**overrides: object) -> MeasurementRecordV2:
values: dict[str, object] = {
"identity": _baseline(),
"family_decision_vocabulary": ("allow", "deny"),
"family_decision_counts": (ReasonCount("allow", 1), ReasonCount("deny", 1)),
"neutral_outcome_counts": (
OutcomeCount(NeutralOutcome.VIOLATION, 1),
OutcomeCount(NeutralOutcome.SATISFIED, 1),
OutcomeCount(NeutralOutcome.INDETERMINATE, 0),
),
"evidence_basis_counts": (
BasisCount(EvidenceBasis.DETERMINISTIC, 2),
BasisCount(EvidenceBasis.MODEL_CORROBORATED, 0),
BasisCount(EvidenceBasis.EXECUTION_BACKED, 0),
),
"metrics": _metrics(),
"stage_funnel": _funnel(),
"instrument_failures": (),
"model_outcome_counts": (ReasonCount("failure", 1), ReasonCount("success", 1)),
"model_failures": ("timeout",),
"runtime_ms": 12,
"prerequisite_readiness": _readiness(),
}
values.update(overrides)
values.pop("record_digest", None)
return create_measurement_record(**values) # type: ignore[arg-type]
def _ref(label: str, *, byte_length: int = 12) -> canonical.ContentRef:
return canonical.ContentRef(
label,
_digest(f"content:{label}"),
"application/json",
byte_length,
_digest(f"retention:{label}"),
_digest(f"receipt:{label}"),
)
def _report(measurement: MeasurementRecordV2 | None = None, **overrides: object) -> RepositoryHoldoutReport:
record = measurement or _record()
values: dict[str, object] = {
"component_digest": record.identity.model_program_digests[0],
"role": ReadinessEvidenceUse.REPOSITORY_HOLDOUT,
"training_population_manifest": _ref("training"),
"holdout_population_manifest": _ref("holdout"),
"training_repository_digests": (_digest("repo:training"),),
"holdout_repository_digests": (_digest("repo:holdout"),),
"fitting_lineage_manifest_digest": _digest("fitting-lineage"),
"measurement": record,
}
values.update(overrides)
values.pop("report_digest", None)
return create_repository_holdout_report(**values) # type: ignore[arg-type]
def test_complete_measurement_record_is_canonical_and_replayable() -> None:
record = _record()
validate_measurement_record(record)
assert record.record_digest == canonical.content_digest(measurement_record_value(record))
assert tuple(item.metric_id for item in record.metrics) == tuple(MetricId)
assert record.record_digest != record.identity.population_digest
def test_record_requires_every_fixed_metric_and_complete_count_fields() -> None:
record = _record()
with pytest.raises(MeasurementRecordError, match="standard metric"):
replace(record, metrics=record.metrics[:-1], record_digest="")
with pytest.raises(MeasurementRecordError, match="outcome count"):
replace(record, neutral_outcome_counts=record.neutral_outcome_counts[:-1], record_digest="")
with pytest.raises(MeasurementRecordError, match="basis count"):
replace(record, evidence_basis_counts=record.evidence_basis_counts[:-1], record_digest="")
def test_decision_vocabulary_counts_and_funnel_totals_must_agree() -> None:
record = _record()
with pytest.raises(MeasurementRecordError, match="decision vocabulary"):
replace(record, family_decision_counts=(ReasonCount("unknown", 2),), record_digest="")
with pytest.raises(MeasurementRecordError, match="question count"):
replace(record, family_decision_counts=(ReasonCount("allow", 1),), record_digest="")
with pytest.raises(MeasurementRecordError, match="determinate"):
replace(
record,
neutral_outcome_counts=(
OutcomeCount(NeutralOutcome.VIOLATION, 0),
OutcomeCount(NeutralOutcome.SATISFIED, 1),
OutcomeCount(NeutralOutcome.INDETERMINATE, 1),
),
record_digest="",
)
def test_later_measurement_carries_canonical_prerequisite_results_and_digests() -> None:
record = _record()
assert tuple(item.stage for item in record.prerequisite_readiness) == tuple(ReadinessStage)[:5]
with pytest.raises(MeasurementRecordError, match="prerequisite readiness"):
replace(record, prerequisite_readiness=record.prerequisite_readiness[:-1], record_digest="")
with pytest.raises(MeasurementRecordError, match="readiness digest"):
replace(
record,
prerequisite_readiness=(replace(record.prerequisite_readiness[0], result_digest="bad"),) + record.prerequisite_readiness[1:],
record_digest="",
)
def test_clean_or_zero_looking_results_require_read_inputs_and_passed_stages() -> None:
clean = _record(
family_decision_counts=(ReasonCount("allow", 2),),
neutral_outcome_counts=(
OutcomeCount(NeutralOutcome.VIOLATION, 0),
OutcomeCount(NeutralOutcome.SATISFIED, 2),
OutcomeCount(NeutralOutcome.INDETERMINATE, 0),
),
metrics=_metrics(model_numerator=2, model_denominator=2),
stage_funnel=_funnel(findings=0),
model_outcome_counts=(ReasonCount("success", 2),),
model_failures=(),
)
with pytest.raises(MeasurementRecordError, match="tracked input"):
replace(clean, stage_funnel=_funnel(inputs_read=0, findings=0), record_digest="")
with pytest.raises(MeasurementRecordError, match="passed readiness"):
replace(clean, prerequisite_readiness=_readiness(result=StageResult.FAILED), record_digest="")
def test_named_instrument_failure_cannot_make_an_all_zero_run_valid() -> None:
zero_funnel = CoverageFunnel(
inputs_read=0,
questions_formed=0,
sites_considered=0,
sites_recognized=0,
transitions_materialized=0,
security_objects_constructed=0,
constraints_evaluated=0,
determinate_outcomes=0,
findings_emitted=0,
relation_status_counts=tuple(RelationStatusCount(status, 0) for status in RelationStatus),
unresolved_by_reason=(),
)
zero_metrics = tuple(evaluate_metric(rule, numerator=0, denominator=0) for rule in standard_metric_rules(_scopes()))
with pytest.raises(MeasurementRecordError, match="tracked input"):
_record(
family_decision_counts=(),
neutral_outcome_counts=tuple(OutcomeCount(outcome, 0) for outcome in NeutralOutcome),
evidence_basis_counts=tuple(BasisCount(basis, 0) for basis in EvidenceBasis),
metrics=zero_metrics,
stage_funnel=zero_funnel,
instrument_failures=("frontend_failed",),
model_outcome_counts=(),
model_failures=(),
prerequisite_readiness=_readiness(result=StageResult.FAILED),
)
with pytest.raises(MeasurementRecordError, match="passed readiness"):
_record(
family_decision_counts=(),
neutral_outcome_counts=tuple(OutcomeCount(outcome, 0) for outcome in NeutralOutcome),
evidence_basis_counts=tuple(BasisCount(basis, 0) for basis in EvidenceBasis),
metrics=zero_metrics,
stage_funnel=replace(zero_funnel, inputs_read=1),
instrument_failures=("one_optional_input_failed",),
model_outcome_counts=(),
model_failures=(),
prerequisite_readiness=_readiness(result=StageResult.FAILED),
)
def test_nonzero_partial_failed_measurement_preserves_named_failures() -> None:
record = _record(
instrument_failures=("one_optional_input_failed",),
prerequisite_readiness=_readiness(result=StageResult.FAILED),
)
validate_measurement_record(record)
assert record.instrument_failures == ("one_optional_input_failed",)
@pytest.mark.parametrize("mixed_denominators", [False, True])
def test_zero_outputs_with_positive_or_mixed_denominators_require_instrument_proof(mixed_denominators: bool) -> None:
zero_funnel = CoverageFunnel(
inputs_read=0,
questions_formed=0,
sites_considered=0,
sites_recognized=0,
transitions_materialized=0,
security_objects_constructed=0,
constraints_evaluated=0,
determinate_outcomes=0,
findings_emitted=0,
relation_status_counts=tuple(RelationStatusCount(status, 0) for status in RelationStatus),
unresolved_by_reason=(),
)
metrics = tuple(
evaluate_metric(rule, numerator=0, denominator=0 if mixed_denominators and index % 2 == 0 else 2)
for index, rule in enumerate(standard_metric_rules(_scopes()))
)
model_denominator = next(item.denominator for item in metrics if item.metric_id is MetricId.MODEL_SUCCESS_RATE)
with pytest.raises(MeasurementRecordError, match="tracked input"):
_record(
family_decision_counts=(),
neutral_outcome_counts=tuple(OutcomeCount(outcome, 0) for outcome in NeutralOutcome),
evidence_basis_counts=tuple(BasisCount(basis, 0) for basis in EvidenceBasis),
metrics=metrics,
stage_funnel=zero_funnel,
instrument_failures=("frontend_failed",),
model_outcome_counts=(ReasonCount("failure", model_denominator),) if model_denominator else (),
model_failures=("timeout",) if model_denominator else (),
prerequisite_readiness=_readiness(result=StageResult.FAILED),
)
def test_zero_output_is_valid_with_reads_and_passed_prerequisites() -> None:
funnel = CoverageFunnel(
inputs_read=1,
questions_formed=0,
sites_considered=0,
sites_recognized=0,
transitions_materialized=0,
security_objects_constructed=0,
constraints_evaluated=0,
determinate_outcomes=0,
findings_emitted=0,
relation_status_counts=tuple(RelationStatusCount(status, 0) for status in RelationStatus),
unresolved_by_reason=(),
)
metrics = tuple(evaluate_metric(rule, numerator=0, denominator=2) for rule in standard_metric_rules(_scopes()))
record = _record(
family_decision_counts=(),
neutral_outcome_counts=tuple(OutcomeCount(outcome, 0) for outcome in NeutralOutcome),
evidence_basis_counts=tuple(BasisCount(basis, 0) for basis in EvidenceBasis),
metrics=metrics,
stage_funnel=funnel,
instrument_failures=(),
model_outcome_counts=(ReasonCount("failure", 2),),
model_failures=("timeout",),
prerequisite_readiness=_readiness(),
)
validate_measurement_record(record)
def test_failures_runtime_and_record_identity_are_not_optional_or_forgeable() -> None:
record = _record()
with pytest.raises(MeasurementRecordError, match="instrument_failures"):
replace(record, instrument_failures=None, record_digest="") # type: ignore[arg-type]
with pytest.raises(MeasurementRecordError, match="model_failures"):
replace(record, model_failures=None, record_digest="") # type: ignore[arg-type]
with pytest.raises(MeasurementRecordError, match="runtime"):
replace(record, runtime_ms=0, record_digest="")
with pytest.raises(MeasurementRecordError, match="record digest"):
replace(record, record_digest=_digest("forged-record"))
with pytest.raises(MeasurementRecordError, match="record digest"):
replace(record, record_digest="")
def test_model_outcomes_and_failures_reconcile_with_fixed_metric() -> None:
with pytest.raises(MeasurementRecordError, match="model outcome counts"):
_record(model_outcome_counts=(ReasonCount("success", 1),))
with pytest.raises(MeasurementRecordError, match="named model_failures"):
_record(model_failures=())
def test_model_success_outcome_count_must_equal_metric_numerator() -> None:
canonical = _record(
model_outcome_counts=(ReasonCount("failure", 1), ReasonCount("success", 1)),
model_failures=("timeout",),
)
validate_measurement_record(canonical)
with pytest.raises(MeasurementRecordError, match="successful model outcome"):
_record(model_outcome_counts=(ReasonCount("success", 2),), model_failures=("timeout",))
with pytest.raises(MeasurementRecordError, match="closed success/failure vocabulary"):
_record(
model_outcome_counts=(ReasonCount("failure", 1), ReasonCount("unknown", 1)),
model_failures=("timeout",),
)
def test_repository_holdout_report_binds_disjoint_manifests_and_measurement() -> None:
report = _report()
validate_repository_holdout_report(report)
assert report.role is ReadinessEvidenceUse.REPOSITORY_HOLDOUT
assert report.report_digest == canonical.content_digest(repository_holdout_report_value(report))
assert report.report_digest != report.measurement.record_digest
def test_family_decision_counts_follow_declared_not_lexical_order() -> None:
record = _record(
family_decision_vocabulary=("deny", "allow"),
family_decision_counts=(ReasonCount("deny", 1), ReasonCount("allow", 1)),
)
validate_measurement_record(record)
assert tuple(item.reason for item in record.family_decision_counts) == record.family_decision_vocabulary
def test_holdout_report_refuses_wrong_role_overlap_or_empty_manifests() -> None:
report = _report()
with pytest.raises(MeasurementRecordError, match="repository_holdout"):
replace(report, role=ReadinessEvidenceUse.DEVELOPMENT_POPULATION, report_digest="")
with pytest.raises(MeasurementRecordError, match="disjoint"):
replace(report, holdout_repository_digests=report.training_repository_digests, report_digest="")
with pytest.raises(MeasurementRecordError, match="nonempty"):
replace(report, holdout_repository_digests=(), report_digest="")
with pytest.raises(MeasurementRecordError, match="nonzero"):
replace(report, holdout_population_manifest=_ref("holdout-zero", byte_length=0), report_digest="")
def test_holdout_report_refuses_component_manifest_and_identity_substitution() -> None:
report = _report()
with pytest.raises(MeasurementRecordError, match="component"):
replace(report, component_digest=_digest("another-component"), report_digest="")
with pytest.raises(MeasurementRecordError, match="distinct"):
replace(report, holdout_population_manifest=report.training_population_manifest, report_digest="")
with pytest.raises(MeasurementRecordError, match="report digest"):
replace(report, report_digest=_digest("forged-report"))
with pytest.raises(MeasurementRecordError, match="report digest"):
replace(report, report_digest="")
with pytest.raises(MeasurementRecordError, match="distinct from measurement"):
replace(report, report_digest=report.measurement.record_digest)