feat(detection-core): apply bounded model corroboration

This commit is contained in:
Marius Ciepluch
2026-10-31 16:57:11 +01:00
parent d28998b262
commit a1800c0c74
3 changed files with 516 additions and 4 deletions
+318 -3
View File
@@ -3,12 +3,17 @@
from __future__ import annotations
import json
from dataclasses import dataclass
from dataclasses import dataclass, replace
import pytest
from detection_core_fixtures import InMemoryContentResolver, stored_artifact
from detection_core_fixtures import (
InMemoryContentResolver,
InMemoryPopulationCustody,
custody_contract,
stored_artifact,
)
from openultrasast.core import canonical, evidence, family, model, provenance, semantics
from openultrasast.core import canonical, evidence, family, model, model_apply, provenance, semantics
def _digest(value: str) -> str:
@@ -326,3 +331,313 @@ def test_core_model_contract_has_no_runtime_dependency_imports() -> None:
assert "import dspy" not in source
assert "litellm" not in source
assert "openai" not in source
@dataclass(frozen=True)
class ApplicationCase:
contract: family.FamilyContract
record: evidence.EvidenceRecord
request: model.ModelRequest
result: model.ModelResult
resolver: InMemoryContentResolver
custody: InMemoryPopulationCustody
custody_policy: provenance.PopulationCustodyPolicy
custodian: str
def _append_model_use(
custody: InMemoryPopulationCustody,
policy: provenance.PopulationCustodyPolicy,
custodian: str,
*,
arm_id: str,
purpose: provenance.LineagePurpose,
artifact_digest: str,
labels_accessed: bool = False,
) -> provenance.LineageUse:
population = custody.current_log(_digest("population"))
request = provenance.PopulationUseRequest(
population.population_digest,
arm_id,
purpose,
labels_accessed,
artifact_digest,
population.log_digest,
)
receipt = custody.append(request, custodian)
provenance.validate_population_custody_log(custody.current_log(population.population_digest), policy)
return provenance.LineageUse(
purpose,
population.population_digest,
labels_accessed,
artifact_digest,
receipt.receipt_digest,
)
def _application_case(
*,
contract: family.FamilyContract | None = None,
record: evidence.EvidenceRecord | None = None,
failed: bool = False,
) -> ApplicationCase:
contract = contract or _contract()
record = record or _record(contract)
question = contract.residual_questions[0]
completeness = model.completeness_snapshot(record, contract)
bundle = model.derive_model_input_bundle_digest(record, contract, question, completeness)
initial, policy, custodian = custody_contract()
custody = InMemoryPopulationCustody(initial, policy)
artifacts = (
(provenance.LineagePurpose.PROMPT, _digest("prompt")),
(provenance.LineagePurpose.EXAMPLE, _digest("examples")),
(provenance.LineagePurpose.FITTED_MODEL, _digest("fitted")),
(provenance.LineagePurpose.DECISION_MAPPING, _digest("mapping")),
)
uses = tuple(
_append_model_use(
custody,
policy,
custodian,
arm_id="model-arm",
purpose=purpose,
artifact_digest=artifact,
)
for purpose, artifact in artifacts
)
lineage = provenance.LineageManifest(
"model-arm",
bundle,
uses,
provenance.derive_lineage_manifest_digest("model-arm", bundle, uses),
)
program = model.make_model_program_declaration(
family_contract_digest=family.family_contract_digest(contract),
provider="provider",
model_name="model-v1",
program_digest=_digest("program"),
prompt_digest=_digest("prompt"),
example_set_digest=_digest("examples"),
fitted_component_digest=_digest("fitted"),
configuration_digest=_digest("model-configuration"),
adapter_revision="adapter-v1",
decision_mapping_digest=_digest("mapping"),
input_bundle_digest=bundle,
lineage_manifest_digest=lineage.manifest_digest,
)
request = model.make_model_request(
record=record,
contract=contract,
residual_question_id=question.question_id,
program=program,
lineage=lineage,
custody_policy_digest=policy.policy_digest,
proposal_program_digests=(_digest("proposer"),),
trusted_response_store_identities=("trusted-store",),
maximum_response_bytes=256,
)
response = model.make_preserved_model_response(
normalized_fields=(semantics.FieldValue("decision", semantics.FieldKind.TEXT, "unsafe"),),
inline_redacted_text="[redacted] unsafe",
artifact=None,
redaction_policy_digest=_digest("redaction-policy"),
)
result = model.make_model_result(
request=request,
status=model.ModelStatus.FAILED if failed else model.ModelStatus.SUCCEEDED,
response=response,
family_decision=None if failed else "unsafe",
failure_reason="provider_timeout" if failed else None,
)
return ApplicationCase(contract, record, request, result, InMemoryContentResolver({}), custody, policy, custodian)
def test_apply_model_result_preserves_packet_and_adds_only_model_corroboration() -> None:
case = _application_case()
packet_digest = evidence.derive_packet_digest(case.record.packet)
derived = model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)
assert isinstance(derived, evidence.EvidenceRecord)
assert derived.packet is case.record.packet
assert evidence.derive_packet_digest(derived.packet) == packet_digest
assert derived.decision.basis is evidence.EvidenceBasis.MODEL_CORROBORATED
assert derived.decision.family_code == "unsafe"
assert derived.decision.provenance.model_result_digest == case.result.result_digest
assert derived.decision.provenance.proof_record_digest is None
def test_failed_model_result_preserves_the_deterministic_record() -> None:
case = _application_case(failed=True)
derived = model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)
assert derived is case.record
def _incomplete_application_case() -> ApplicationCase:
base_contract = _contract()
unknown = family.DecisionRule(
"unknown",
evidence.NeutralOutcome.INDETERMINATE,
("flow",),
(semantics.RelationStatus.INCOMPLETE,),
)
contract = replace(base_contract, decisions=(*base_contract.decisions, unknown))
complete = _record(contract)
relation = complete.packet.transitions[0]
incomplete_relation = semantics.make_relation(
kind=relation.kind,
role=relation.role,
source=relation.source,
target=relation.target,
location=relation.location,
status=semantics.RelationStatus.INCOMPLETE,
conditions=relation.conditions,
reasons=("missing",),
provenance=relation.provenance,
)
packet = replace(complete.packet, transitions=(incomplete_relation,), completeness_reasons=("missing",))
decision = evidence.Decision(
"unknown",
evidence.NeutralOutcome.INDETERMINATE,
evidence.EvidenceBasis.DETERMINISTIC,
packet.scope,
("missing",),
evidence.derive_packet_digest(packet),
provenance.DecisionProvenance(None, None, None, None, None, None),
)
return _application_case(contract=contract, record=evidence.EvidenceRecord(packet, decision, ""))
def test_incomplete_residual_prerequisite_remains_indeterminate() -> None:
case = _incomplete_application_case()
derived = model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)
assert derived is case.record
assert derived.decision.outcome is evidence.NeutralOutcome.INDETERMINATE
assert derived.decision.basis is evidence.EvidenceBasis.DETERMINISTIC
def test_model_application_refuses_proposer_adjudicator_collision_and_family_substitution() -> None:
case = _application_case()
object.__setattr__(case.request, "proposal_program_digests", (case.request.program.program_digest,))
with pytest.raises(model.ModelContractError, match="propose|adjudicat"):
model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)
case = _application_case()
other_contract = replace(case.contract, family_id="other-family")
with pytest.raises(model.ModelContractError, match="family|contract"):
model_apply.apply_model_result(
case.record,
other_contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)
def test_model_application_refuses_stale_or_label_contaminated_lineage() -> None:
case = _application_case()
_append_model_use(
case.custody,
case.custody_policy,
case.custodian,
arm_id="model-arm",
purpose=provenance.LineagePurpose.PROMPT,
artifact_digest=case.request.program.prompt_digest,
)
with pytest.raises(model.ModelContractError, match="omitted|current"):
model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)
case = _application_case()
_append_model_use(
case.custody,
case.custody_policy,
case.custodian,
arm_id="other-arm",
purpose=provenance.LineagePurpose.DECISION_MAPPING,
artifact_digest=case.request.program.decision_mapping_digest,
labels_accessed=True,
)
with pytest.raises(model.ModelContractError, match="label|contaminat|spent"):
model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)
def test_model_application_refuses_custody_policy_and_result_substitution() -> None:
case = _application_case()
other_policy = replace(case.custody_policy, policy_digest=_digest("other-policy"))
with pytest.raises(model.ModelContractError, match="custody policy"):
model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
other_policy,
)
case = _application_case()
object.__setattr__(case.result, "packet_digest", _digest("other-packet"))
with pytest.raises(model.ModelContractError, match="packet"):
model_apply.apply_model_result(
case.record,
case.contract,
case.request,
case.result,
case.resolver,
case.custody,
case.custody_policy,
)