2026-10-31 16:08:05 +01:00
|
|
|
"""Reviewed fact-effect materialization and reproducible provenance."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass, replace
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
from detection_core_fixtures import InMemoryContentResolver, InMemoryPopulationCustody, custody_contract, stored_artifact
|
|
|
|
|
|
2026-10-31 16:33:53 +01:00
|
|
|
from openultrasast.core import canonical, evidence, provenance
|
2026-10-31 16:08:05 +01:00
|
|
|
from openultrasast.core.evidence import NeutralOutcome
|
|
|
|
|
from openultrasast.core.factapply import FactApplicationError, apply_fact
|
|
|
|
|
from openultrasast.core.factmatch import (
|
|
|
|
|
FactMatchEvidence,
|
|
|
|
|
StructuralObservation,
|
|
|
|
|
derive_fact_match_digest,
|
|
|
|
|
fact_match_bytes,
|
|
|
|
|
structural_observation_bytes,
|
|
|
|
|
)
|
|
|
|
|
from openultrasast.core.factpack import (
|
|
|
|
|
FACT_PACK_SCHEMA,
|
|
|
|
|
FactPack,
|
|
|
|
|
FactPackValidationError,
|
|
|
|
|
IndependenceAttestation,
|
|
|
|
|
KnowledgeEvidence,
|
|
|
|
|
KnowledgeEvidenceKind,
|
|
|
|
|
RelationEffect,
|
|
|
|
|
ReviewIndependencePolicy,
|
|
|
|
|
SemanticFact,
|
|
|
|
|
StructuralRequirement,
|
|
|
|
|
StructuralRequirementKind,
|
|
|
|
|
derive_fact_pack_content_digest,
|
|
|
|
|
derive_independence_attestation_digest,
|
|
|
|
|
derive_review_evidence_digest,
|
|
|
|
|
derive_review_independence_policy_digest,
|
|
|
|
|
)
|
2026-10-31 16:33:53 +01:00
|
|
|
from openultrasast.core.factvalidate import FactRelationValidationError, validate_fact_relations
|
2026-10-31 16:08:05 +01:00
|
|
|
from openultrasast.core.family import (
|
2026-10-31 16:33:53 +01:00
|
|
|
MISSING_REVIEWED_DEPENDENCY,
|
2026-10-31 16:08:05 +01:00
|
|
|
DecisionRule,
|
|
|
|
|
FamilyContract,
|
|
|
|
|
FieldSpec,
|
|
|
|
|
ObjectSpec,
|
|
|
|
|
RelationOriginPolicy,
|
|
|
|
|
ResidualQuestion,
|
|
|
|
|
RoleSpec,
|
2026-10-31 16:33:53 +01:00
|
|
|
derive_security_object_id,
|
2026-10-31 16:08:05 +01:00
|
|
|
family_contract_digest,
|
|
|
|
|
)
|
|
|
|
|
from openultrasast.core.semantics import (
|
2026-10-31 16:33:53 +01:00
|
|
|
EvidenceProvenance,
|
2026-10-31 16:08:05 +01:00
|
|
|
FieldKind,
|
2026-10-31 16:33:53 +01:00
|
|
|
FieldValue,
|
2026-10-31 16:08:05 +01:00
|
|
|
ProvenanceKind,
|
|
|
|
|
RelationKind,
|
|
|
|
|
RelationStatus,
|
2026-10-31 16:33:53 +01:00
|
|
|
SemanticNode,
|
2026-10-31 16:08:05 +01:00
|
|
|
SemanticRelation,
|
|
|
|
|
SourceLocation,
|
2026-10-31 16:33:53 +01:00
|
|
|
make_relation,
|
2026-10-31 16:08:05 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
class ApplyCase:
|
|
|
|
|
pack: FactPack
|
|
|
|
|
contract: FamilyContract
|
|
|
|
|
match: FactMatchEvidence
|
|
|
|
|
match_reference: canonical.ContentRef
|
|
|
|
|
lineages: tuple[provenance.LineageManifest, ...]
|
|
|
|
|
custody: InMemoryPopulationCustody
|
|
|
|
|
custody_policy: provenance.PopulationCustodyPolicy
|
|
|
|
|
review_policy: ReviewIndependencePolicy
|
|
|
|
|
resolver: InMemoryContentResolver
|
|
|
|
|
population_digest: str
|
|
|
|
|
|
|
|
|
|
|
2026-10-31 16:33:53 +01:00
|
|
|
class MultiPopulationCustody(InMemoryPopulationCustody):
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
logs: tuple[provenance.PopulationCustodyLog, ...],
|
|
|
|
|
policy: provenance.PopulationCustodyPolicy,
|
|
|
|
|
) -> None:
|
|
|
|
|
self._logs = {log.population_digest: log for log in logs}
|
|
|
|
|
self._multi_policy = policy
|
|
|
|
|
|
|
|
|
|
def current_log(self, population_digest: str) -> provenance.PopulationCustodyLog:
|
|
|
|
|
try:
|
|
|
|
|
return self._logs[population_digest]
|
|
|
|
|
except KeyError as exc:
|
|
|
|
|
raise provenance.PopulationCustodyError("population custody log not found") from exc
|
|
|
|
|
|
|
|
|
|
def append(self, request: provenance.PopulationUseRequest, custodian: str) -> provenance.PopulationUseReceipt:
|
|
|
|
|
prior = self.current_log(request.population_digest)
|
|
|
|
|
receipt = provenance.record_population_use(request, prior, custodian, self._multi_policy)
|
|
|
|
|
self._logs[request.population_digest] = provenance.append_population_use(prior, receipt, self._multi_policy)
|
|
|
|
|
return receipt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _contract(*, fact_only: bool = False) -> FamilyContract:
|
|
|
|
|
origin = RelationOriginPolicy.EXCLUDED_DEPENDENCY_FACT_ONLY if fact_only else RelationOriginPolicy.PRODUCT_OR_REVIEWED_FACT
|
2026-10-31 16:08:05 +01:00
|
|
|
return FamilyContract(
|
|
|
|
|
"reference-family",
|
|
|
|
|
"1",
|
2026-10-31 16:33:53 +01:00
|
|
|
evidence.EVIDENCE_PACKET_SCHEMA,
|
2026-10-31 16:08:05 +01:00
|
|
|
("python",),
|
|
|
|
|
ObjectSpec("resource", (FieldSpec("name", FieldKind.TEXT, True),), ("name",)),
|
|
|
|
|
(
|
2026-10-31 16:33:53 +01:00
|
|
|
RoleSpec("flow", (), (RelationKind.CALL,), origin),
|
2026-10-31 16:08:05 +01:00
|
|
|
RoleSpec("source", (FieldSpec("name", FieldKind.TEXT, False),), (), RelationOriginPolicy.PRODUCT_OR_REVIEWED_FACT),
|
|
|
|
|
RoleSpec("target", (FieldSpec("name", FieldKind.TEXT, False),), (), RelationOriginPolicy.PRODUCT_OR_REVIEWED_FACT),
|
|
|
|
|
),
|
2026-10-31 16:33:53 +01:00
|
|
|
(
|
|
|
|
|
DecisionRule("safe", NeutralOutcome.SATISFIED, ("flow",), (RelationStatus.COMPLETE,)),
|
|
|
|
|
DecisionRule("unknown", NeutralOutcome.INDETERMINATE, (), (RelationStatus.INCOMPLETE, RelationStatus.UNRESOLVED)),
|
|
|
|
|
),
|
|
|
|
|
(ResidualQuestion("classify", ("flow",), ("safe", "unknown")),),
|
|
|
|
|
("missing", MISSING_REVIEWED_DEPENDENCY),
|
2026-10-31 16:08:05 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _append_use(
|
|
|
|
|
custody: InMemoryPopulationCustody,
|
|
|
|
|
custodian: str,
|
|
|
|
|
purpose: provenance.LineagePurpose,
|
|
|
|
|
subject: str,
|
|
|
|
|
*,
|
|
|
|
|
labels_accessed: bool,
|
|
|
|
|
arm_id: str,
|
2026-10-31 16:33:53 +01:00
|
|
|
population_digest: str | None = None,
|
2026-10-31 16:08:05 +01:00
|
|
|
) -> provenance.LineageUse:
|
2026-10-31 16:33:53 +01:00
|
|
|
current = custody.current_log(population_digest or canonical.content_digest("population"))
|
2026-10-31 16:08:05 +01:00
|
|
|
request = provenance.PopulationUseRequest(
|
|
|
|
|
current.population_digest,
|
|
|
|
|
arm_id,
|
|
|
|
|
purpose,
|
|
|
|
|
labels_accessed,
|
|
|
|
|
subject,
|
|
|
|
|
current.log_digest,
|
|
|
|
|
)
|
|
|
|
|
receipt = custody.append(request, custodian)
|
|
|
|
|
return provenance.LineageUse(purpose, current.population_digest, labels_accessed, subject, receipt.receipt_digest)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _manifest(arm_id: str, subject: str, uses: tuple[provenance.LineageUse, ...]) -> provenance.LineageManifest:
|
|
|
|
|
digest = provenance.derive_lineage_manifest_digest(arm_id, subject, uses)
|
|
|
|
|
return provenance.LineageManifest(arm_id, subject, uses, digest)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _review_policy(reviewer: str) -> ReviewIndependencePolicy:
|
|
|
|
|
stores = ("trusted-store",)
|
|
|
|
|
digest = derive_review_independence_policy_digest((reviewer,), (), stores, 4096)
|
|
|
|
|
return ReviewIndependencePolicy((reviewer,), (), stores, 4096, digest)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _review_evidence() -> tuple[KnowledgeEvidence, InMemoryContentResolver, str]:
|
|
|
|
|
producer = canonical.content_digest("producer")
|
|
|
|
|
reviewer = canonical.content_digest("reviewer")
|
|
|
|
|
content, content_resolver = stored_artifact(b'{"reviewed":"content"}', artifact_id="knowledge")
|
|
|
|
|
disclosure, disclosure_resolver = stored_artifact(b'{"conflicts":[]}', artifact_id="conflicts")
|
|
|
|
|
provisional = IndependenceAttestation(content.content_digest, producer, reviewer, disclosure, "0" * 64)
|
|
|
|
|
attestation = replace(provisional, attestation_digest=derive_independence_attestation_digest(provisional))
|
|
|
|
|
evidence = KnowledgeEvidence(
|
|
|
|
|
KnowledgeEvidenceKind.SOURCE_CODE,
|
|
|
|
|
content,
|
|
|
|
|
producer,
|
|
|
|
|
reviewer,
|
|
|
|
|
attestation,
|
|
|
|
|
"0" * 64,
|
|
|
|
|
)
|
|
|
|
|
artifacts = {**content_resolver.artifacts, **disclosure_resolver.artifacts}
|
|
|
|
|
return evidence, InMemoryContentResolver(artifacts), reviewer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _requirements() -> tuple[StructuralRequirement, ...]:
|
|
|
|
|
return (
|
|
|
|
|
StructuralRequirement("qualified", StructuralRequirementKind.QUALIFIED_NAME, "callee", "pkg.Client.call"),
|
|
|
|
|
StructuralRequirement("shape", StructuralRequirementKind.AST_SHAPE, "shape", {"node": "call"}),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _case(
|
|
|
|
|
*,
|
|
|
|
|
effects: tuple[RelationEffect, ...] | None = None,
|
|
|
|
|
missing_requirement: str | None = None,
|
|
|
|
|
matcher_labels: bool = False,
|
|
|
|
|
promotion_labels: bool = False,
|
|
|
|
|
omitted_promotion_labels: bool = False,
|
2026-10-31 16:33:53 +01:00
|
|
|
fact_only: bool = False,
|
2026-10-31 16:08:05 +01:00
|
|
|
) -> ApplyCase:
|
2026-10-31 16:33:53 +01:00
|
|
|
contract = _contract(fact_only=fact_only)
|
2026-10-31 16:08:05 +01:00
|
|
|
evidence, review_resolver, reviewer = _review_evidence()
|
|
|
|
|
review_policy = _review_policy(reviewer)
|
|
|
|
|
effects = effects or (RelationEffect(RelationKind.CALL, "flow", "source", "target", ("matched",)),)
|
|
|
|
|
fact = SemanticFact("stable.fact.v1", "flow", _requirements(), effects)
|
|
|
|
|
provisional_pack = FactPack(
|
|
|
|
|
FACT_PACK_SCHEMA,
|
|
|
|
|
family_contract_digest(contract),
|
|
|
|
|
contract.family_id,
|
|
|
|
|
"python",
|
|
|
|
|
"python",
|
|
|
|
|
(fact,),
|
|
|
|
|
"0" * 64,
|
|
|
|
|
reviewer,
|
|
|
|
|
(evidence,),
|
|
|
|
|
"0" * 64,
|
|
|
|
|
review_policy.policy_digest,
|
|
|
|
|
)
|
|
|
|
|
pack_digest = derive_fact_pack_content_digest(provisional_pack)
|
|
|
|
|
initial, custody_policy, custodian = custody_contract()
|
|
|
|
|
custody = InMemoryPopulationCustody(initial, custody_policy)
|
|
|
|
|
fact_uses: tuple[provenance.LineageUse, ...] = (
|
|
|
|
|
_append_use(
|
|
|
|
|
custody,
|
|
|
|
|
custodian,
|
|
|
|
|
provenance.LineagePurpose.SEMANTIC_FACT,
|
|
|
|
|
pack_digest,
|
|
|
|
|
labels_accessed=False,
|
|
|
|
|
arm_id="fact-arm",
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
if promotion_labels:
|
|
|
|
|
fact_uses += (
|
|
|
|
|
_append_use(
|
|
|
|
|
custody,
|
|
|
|
|
custodian,
|
|
|
|
|
provenance.LineagePurpose.PROMOTION,
|
|
|
|
|
pack_digest,
|
|
|
|
|
labels_accessed=True,
|
|
|
|
|
arm_id="fact-arm",
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
fact_lineage = _manifest("fact-arm", pack_digest, fact_uses)
|
|
|
|
|
if omitted_promotion_labels:
|
|
|
|
|
_append_use(
|
|
|
|
|
custody,
|
|
|
|
|
custodian,
|
|
|
|
|
provenance.LineagePurpose.PROMOTION,
|
|
|
|
|
pack_digest,
|
|
|
|
|
labels_accessed=True,
|
|
|
|
|
arm_id="fact-arm",
|
|
|
|
|
)
|
|
|
|
|
evidence = replace(evidence, lineage_manifest_digest=fact_lineage.manifest_digest)
|
|
|
|
|
pack = replace(
|
|
|
|
|
provisional_pack,
|
|
|
|
|
normalized_content_digest=pack_digest,
|
|
|
|
|
review_evidence=(evidence,),
|
|
|
|
|
review_evidence_digest=derive_review_evidence_digest((evidence,)),
|
|
|
|
|
)
|
|
|
|
|
location = SourceLocation("src/product.py", 17, 8, "run")
|
|
|
|
|
observations: list[StructuralObservation] = []
|
|
|
|
|
artifacts = dict(review_resolver.artifacts)
|
|
|
|
|
for requirement in fact.structural_requirements:
|
|
|
|
|
if requirement.requirement_id == missing_requirement:
|
|
|
|
|
continue
|
|
|
|
|
provisional_observation = StructuralObservation(
|
|
|
|
|
requirement.requirement_id,
|
|
|
|
|
location,
|
|
|
|
|
requirement.expected,
|
|
|
|
|
canonical.ContentRef("provisional", "0" * 64, "application/json", 0, "0" * 64, "0" * 64),
|
|
|
|
|
)
|
|
|
|
|
reference, observation_resolver = stored_artifact(
|
|
|
|
|
structural_observation_bytes(provisional_observation),
|
|
|
|
|
artifact_id=f"observation-{requirement.requirement_id}",
|
|
|
|
|
)
|
|
|
|
|
observations.append(replace(provisional_observation, evidence=reference))
|
|
|
|
|
artifacts.update(observation_resolver.artifacts)
|
|
|
|
|
behavior = canonical.content_digest("matcher-behavior-v3")
|
|
|
|
|
matcher_use = _append_use(
|
|
|
|
|
custody,
|
|
|
|
|
custodian,
|
|
|
|
|
provenance.LineagePurpose.MATCHING_BEHAVIOR,
|
|
|
|
|
behavior,
|
|
|
|
|
labels_accessed=matcher_labels,
|
|
|
|
|
arm_id="matcher-arm",
|
|
|
|
|
)
|
|
|
|
|
matcher_lineage = _manifest("matcher-arm", behavior, (matcher_use,))
|
|
|
|
|
provisional_match = FactMatchEvidence(
|
|
|
|
|
pack.normalized_content_digest,
|
|
|
|
|
fact.fact_id,
|
|
|
|
|
location,
|
|
|
|
|
tuple(observations),
|
|
|
|
|
"matcher-v3",
|
|
|
|
|
behavior,
|
|
|
|
|
matcher_lineage.manifest_digest,
|
|
|
|
|
"0" * 64,
|
|
|
|
|
)
|
|
|
|
|
match = replace(provisional_match, match_digest=derive_fact_match_digest(provisional_match))
|
|
|
|
|
match_reference, match_resolver = stored_artifact(fact_match_bytes(match), artifact_id="fact-match")
|
|
|
|
|
artifacts.update(match_resolver.artifacts)
|
|
|
|
|
return ApplyCase(
|
|
|
|
|
pack,
|
|
|
|
|
contract,
|
|
|
|
|
match,
|
|
|
|
|
match_reference,
|
|
|
|
|
(fact_lineage, matcher_lineage),
|
|
|
|
|
custody,
|
|
|
|
|
custody_policy,
|
|
|
|
|
review_policy,
|
|
|
|
|
InMemoryContentResolver(artifacts),
|
|
|
|
|
initial.population_digest,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _apply(case: ApplyCase) -> tuple[SemanticRelation, ...]:
|
|
|
|
|
return apply_fact(
|
|
|
|
|
case.pack,
|
|
|
|
|
case.pack.facts[0].fact_id,
|
|
|
|
|
case.match,
|
|
|
|
|
case.match_reference,
|
|
|
|
|
case.contract,
|
|
|
|
|
case.population_digest,
|
|
|
|
|
case.lineages,
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy,
|
|
|
|
|
case.review_policy,
|
|
|
|
|
case.resolver,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-10-31 16:33:53 +01:00
|
|
|
def _case_with_distinct_evaluation_population() -> ApplyCase:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
source_log = case.custody.current_log(case.population_digest)
|
|
|
|
|
evaluation_digest = canonical.content_digest("distinct-evaluation-population")
|
|
|
|
|
evaluation_log = provenance.empty_population_custody_log(
|
|
|
|
|
evaluation_digest,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
)
|
|
|
|
|
custody = MultiPopulationCustody((source_log, evaluation_log), case.custody_policy)
|
|
|
|
|
return replace(case, custody=custody, population_digest=evaluation_digest)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _case_with_three_populations() -> tuple[ApplyCase, str]:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
custodian = case.custody_policy.reviewed_custodian_digests[0]
|
|
|
|
|
source_log = case.custody.current_log(case.population_digest)
|
|
|
|
|
fact_empty = provenance.empty_population_custody_log(case.population_digest, custodian)
|
|
|
|
|
fact_log = provenance.append_population_use(fact_empty, source_log.receipts[0], case.custody_policy)
|
|
|
|
|
matcher_population = canonical.content_digest("distinct-matcher-population")
|
|
|
|
|
matcher_empty = provenance.empty_population_custody_log(matcher_population, custodian)
|
|
|
|
|
matcher_request = provenance.PopulationUseRequest(
|
|
|
|
|
matcher_population,
|
|
|
|
|
"matcher-arm",
|
|
|
|
|
provenance.LineagePurpose.MATCHING_BEHAVIOR,
|
|
|
|
|
False,
|
|
|
|
|
case.match.matcher_behavior_digest,
|
|
|
|
|
matcher_empty.log_digest,
|
|
|
|
|
)
|
|
|
|
|
matcher_receipt = provenance.record_population_use(matcher_request, matcher_empty, custodian, case.custody_policy)
|
|
|
|
|
matcher_log = provenance.append_population_use(matcher_empty, matcher_receipt, case.custody_policy)
|
|
|
|
|
matcher_use = provenance.LineageUse(
|
|
|
|
|
provenance.LineagePurpose.MATCHING_BEHAVIOR,
|
|
|
|
|
matcher_population,
|
|
|
|
|
False,
|
|
|
|
|
case.match.matcher_behavior_digest,
|
|
|
|
|
matcher_receipt.receipt_digest,
|
|
|
|
|
)
|
|
|
|
|
matcher_lineage = _manifest("matcher-arm", case.match.matcher_behavior_digest, (matcher_use,))
|
|
|
|
|
provisional_match = replace(
|
|
|
|
|
case.match,
|
|
|
|
|
matcher_lineage_manifest_digest=matcher_lineage.manifest_digest,
|
|
|
|
|
match_digest="0" * 64,
|
|
|
|
|
)
|
|
|
|
|
match = replace(provisional_match, match_digest=derive_fact_match_digest(provisional_match))
|
|
|
|
|
match_reference, match_resolver = stored_artifact(fact_match_bytes(match), artifact_id="fact-match")
|
|
|
|
|
evaluation_digest = canonical.content_digest("third-evaluation-population")
|
|
|
|
|
evaluation_log = provenance.empty_population_custody_log(evaluation_digest, custodian)
|
|
|
|
|
custody = MultiPopulationCustody((fact_log, matcher_log, evaluation_log), case.custody_policy)
|
|
|
|
|
artifacts = {**case.resolver.artifacts, **match_resolver.artifacts}
|
|
|
|
|
changed = replace(
|
|
|
|
|
case,
|
|
|
|
|
match=match,
|
|
|
|
|
match_reference=match_reference,
|
|
|
|
|
lineages=(case.lineages[0], matcher_lineage),
|
|
|
|
|
custody=custody,
|
|
|
|
|
resolver=InMemoryContentResolver(artifacts),
|
|
|
|
|
population_digest=evaluation_digest,
|
|
|
|
|
)
|
|
|
|
|
return changed, matcher_population
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _stamp(case: ApplyCase) -> provenance.ProvenanceStamp:
|
|
|
|
|
return provenance.ProvenanceStamp(
|
|
|
|
|
canonical.content_digest("analyzer"),
|
|
|
|
|
family_contract_digest(case.contract),
|
|
|
|
|
canonical.content_digest("fact-set"),
|
|
|
|
|
canonical.content_digest("acquisition"),
|
|
|
|
|
canonical.content_digest("configuration"),
|
|
|
|
|
case.population_digest,
|
|
|
|
|
canonical.content_digest("change-input"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _record(case: ApplyCase, relation: SemanticRelation) -> evidence.EvidenceRecord:
|
|
|
|
|
fields = (FieldValue("name", FieldKind.TEXT, "resource"),)
|
|
|
|
|
scope = evidence.AnalyzedScope(
|
|
|
|
|
"base",
|
|
|
|
|
"head",
|
|
|
|
|
canonical.content_digest("change"),
|
|
|
|
|
(evidence.SourceRegion(case.match.product_location.path, 1, 30),),
|
|
|
|
|
)
|
|
|
|
|
reasons = relation.reasons if relation.status is not RelationStatus.COMPLETE else ()
|
|
|
|
|
packet = evidence.EvidencePacket(
|
|
|
|
|
evidence.EVIDENCE_PACKET_SCHEMA,
|
|
|
|
|
case.contract.family_id,
|
|
|
|
|
family_contract_digest(case.contract),
|
|
|
|
|
derive_security_object_id(case.contract.security_object, fields),
|
|
|
|
|
case.pack.ecosystem,
|
|
|
|
|
scope,
|
|
|
|
|
(relation,),
|
|
|
|
|
(),
|
|
|
|
|
fields,
|
|
|
|
|
reasons,
|
|
|
|
|
_stamp(case),
|
|
|
|
|
)
|
|
|
|
|
outcome = NeutralOutcome.SATISFIED if relation.status is RelationStatus.COMPLETE else NeutralOutcome.INDETERMINATE
|
|
|
|
|
decision = evidence.Decision(
|
|
|
|
|
"safe" if relation.status is RelationStatus.COMPLETE else "unknown",
|
|
|
|
|
outcome,
|
|
|
|
|
evidence.EvidenceBasis.DETERMINISTIC,
|
|
|
|
|
scope,
|
|
|
|
|
reasons,
|
|
|
|
|
evidence.derive_packet_digest(packet),
|
|
|
|
|
provenance.DecisionProvenance(None, None, None, None, None, None),
|
|
|
|
|
)
|
|
|
|
|
return evidence.EvidenceRecord(packet, decision, "")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _validate(case: ApplyCase, record: evidence.EvidenceRecord, *, packs: tuple[FactPack, ...] | None = None) -> None:
|
|
|
|
|
validate_fact_relations(
|
|
|
|
|
record,
|
|
|
|
|
case.contract,
|
|
|
|
|
(case.pack,) if packs is None else packs,
|
|
|
|
|
case.population_digest,
|
|
|
|
|
case.lineages,
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy,
|
|
|
|
|
case.review_policy,
|
|
|
|
|
case.resolver,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-10-31 16:08:05 +01:00
|
|
|
def test_complete_match_materializes_only_declared_effect_with_exact_provenance() -> None:
|
|
|
|
|
case = _case()
|
|
|
|
|
|
|
|
|
|
relations = _apply(case)
|
|
|
|
|
|
|
|
|
|
assert case.match_reference.byte_length > 0
|
|
|
|
|
assert len(relations) == 1
|
|
|
|
|
relation = relations[0]
|
|
|
|
|
assert relation.kind is RelationKind.CALL
|
|
|
|
|
assert relation.role == "flow"
|
|
|
|
|
assert relation.source.role == "source" and relation.target.role == "target"
|
|
|
|
|
assert relation.location == case.match.product_location
|
|
|
|
|
assert relation.conditions == ("matched",)
|
|
|
|
|
assert relation.status is RelationStatus.COMPLETE and relation.reasons == ()
|
|
|
|
|
assert relation.provenance.kind is ProvenanceKind.SEMANTIC_FACT
|
|
|
|
|
assert relation.provenance.source_content_digest == case.match.match_digest
|
|
|
|
|
assert relation.provenance.semantic_pack_digest == case.pack.normalized_content_digest
|
|
|
|
|
assert relation.provenance.semantic_fact_id == case.match.fact_id
|
|
|
|
|
assert relation.provenance.fact_match == case.match_reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_partial_match_preserves_conditions_and_named_incomplete_evidence() -> None:
|
|
|
|
|
case = _case(missing_requirement="shape")
|
|
|
|
|
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
|
|
|
|
|
assert relation.status is RelationStatus.INCOMPLETE
|
|
|
|
|
assert relation.conditions == ("matched",)
|
|
|
|
|
assert "missing_observation:shape" in relation.reasons
|
|
|
|
|
assert "unmet_condition:matched" in relation.reasons
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_conflicting_eligible_effects_are_one_unresolved_ambiguity() -> None:
|
|
|
|
|
effects = (
|
|
|
|
|
RelationEffect(RelationKind.CALL, "flow", "source", "target", ("first",)),
|
|
|
|
|
RelationEffect(RelationKind.CALL, "flow", "source", "target", ("second",)),
|
|
|
|
|
)
|
|
|
|
|
case = _case(effects=effects)
|
|
|
|
|
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
|
|
|
|
|
assert relation.status is RelationStatus.UNRESOLVED
|
|
|
|
|
assert relation.conditions == ("first", "second")
|
|
|
|
|
assert any(reason.startswith("conflicting_complete_candidate:") for reason in relation.reasons)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_match_reference_must_resolve_to_exact_canonical_match_bytes() -> None:
|
|
|
|
|
case = _case()
|
|
|
|
|
wrong, wrong_resolver = stored_artifact(b'{"plausible":"but-wrong"}', artifact_id="wrong-match")
|
|
|
|
|
changed = replace(
|
|
|
|
|
case,
|
|
|
|
|
match_reference=wrong,
|
|
|
|
|
resolver=InMemoryContentResolver({**case.resolver.artifacts, **wrong_resolver.artifacts}),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(FactApplicationError, match="match reference"):
|
|
|
|
|
_apply(changed)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_label_influenced_matching_is_spent_before_relation_is_non_determinate() -> None:
|
|
|
|
|
case = _case(matcher_labels=True)
|
|
|
|
|
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
|
|
|
|
|
assert case.custody.current_log(case.population_digest).status is provenance.PopulationStatus.SPENT
|
|
|
|
|
assert relation.status is RelationStatus.UNRESOLVED
|
|
|
|
|
assert f"protected_population_labels:{case.population_digest}" in relation.reasons
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_label_influenced_promotion_is_spent_and_refused_before_materialization() -> None:
|
|
|
|
|
case = _case(promotion_labels=True)
|
|
|
|
|
|
|
|
|
|
assert case.custody.current_log(case.population_digest).status is provenance.PopulationStatus.SPENT
|
|
|
|
|
with pytest.raises(FactPackValidationError, match="label_access_forbidden"):
|
|
|
|
|
_apply(case)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_omitted_current_promotion_spend_cannot_hide_behind_clean_pack_lineage() -> None:
|
|
|
|
|
case = _case(omitted_promotion_labels=True)
|
|
|
|
|
|
|
|
|
|
assert case.custody.current_log(case.population_digest).status is provenance.PopulationStatus.SPENT
|
|
|
|
|
with pytest.raises(FactApplicationError, match="promotion.*protected_population_labels"):
|
|
|
|
|
_apply(case)
|
2026-10-31 16:33:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_reviewed_dependency_relation_replays_from_every_named_input() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_product_only_dependency_behavior_is_refused() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(reviewed,) = _apply(case)
|
|
|
|
|
product = replace(
|
|
|
|
|
reviewed,
|
|
|
|
|
provenance=EvidenceProvenance(ProvenanceKind.PRODUCT_CODE, case.match.match_digest, None, None, None),
|
|
|
|
|
relation_id="",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="reviewed semantic fact"):
|
|
|
|
|
_validate(case, _record(case, product))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_missing_dependency_fact_remains_named_and_indeterminate() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
location = case.match.product_location
|
|
|
|
|
missing = make_relation(
|
|
|
|
|
kind=RelationKind.CALL,
|
|
|
|
|
role="flow",
|
|
|
|
|
source=SemanticNode("source", "source", location),
|
|
|
|
|
target=SemanticNode("target", "target", location),
|
|
|
|
|
location=location,
|
|
|
|
|
status=RelationStatus.INCOMPLETE,
|
|
|
|
|
conditions=("matched",),
|
|
|
|
|
reasons=(MISSING_REVIEWED_DEPENDENCY,),
|
|
|
|
|
provenance=EvidenceProvenance(ProvenanceKind.PRODUCT_CODE, case.match.match_digest, None, None, None),
|
|
|
|
|
)
|
|
|
|
|
record = _record(case, missing)
|
|
|
|
|
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
assert record.decision.outcome is NeutralOutcome.INDETERMINATE
|
|
|
|
|
assert record.decision.reasons == (MISSING_REVIEWED_DEPENDENCY,)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_undeclared_role_and_invalid_scope() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
undeclared = replace(relation, role="undeclared", relation_id="")
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="undeclared semantic role"):
|
|
|
|
|
_validate(case, _record(case, undeclared))
|
|
|
|
|
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
packet = replace(record.packet, ecosystem="javascript")
|
|
|
|
|
decision = replace(record.decision, packet_digest=evidence.derive_packet_digest(packet))
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="unsupported ecosystem scope"):
|
|
|
|
|
_validate(case, evidence.EvidenceRecord(packet, decision, ""))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_inconsistent_relation_and_fact_identity() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
inconsistent = replace(relation, conditions=("substituted",), relation_id="")
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="does not equal replayed"):
|
|
|
|
|
_validate(case, _record(case, inconsistent))
|
|
|
|
|
|
|
|
|
|
wrong_origin = replace(relation.provenance, semantic_fact_id="other.fact")
|
|
|
|
|
wrong_fact = replace(relation, provenance=wrong_origin, relation_id="")
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="fact identity"):
|
|
|
|
|
_validate(case, _record(case, wrong_fact))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_substituted_resolved_match_bytes() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
wrong_ref, wrong_resolver = stored_artifact(b'{"plausible":"but-wrong"}', artifact_id="wrong-replay-match")
|
|
|
|
|
wrong_origin = replace(
|
|
|
|
|
relation.provenance,
|
|
|
|
|
source_content_digest=wrong_ref.content_digest,
|
|
|
|
|
fact_match=wrong_ref,
|
|
|
|
|
)
|
|
|
|
|
wrong_relation = replace(relation, provenance=wrong_origin, relation_id="")
|
|
|
|
|
changed = replace(
|
|
|
|
|
case,
|
|
|
|
|
resolver=InMemoryContentResolver({**case.resolver.artifacts, **wrong_resolver.artifacts}),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="match artifact"):
|
|
|
|
|
_validate(changed, _record(changed, wrong_relation))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_ambiguous_pack_and_changed_current_custody() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="pack identity is missing or ambiguous"):
|
|
|
|
|
_validate(case, record, packs=(case.pack, case.pack))
|
|
|
|
|
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
provenance.LineagePurpose.PROMOTION,
|
|
|
|
|
case.pack.normalized_content_digest,
|
|
|
|
|
labels_accessed=True,
|
|
|
|
|
arm_id="other-arm",
|
|
|
|
|
)
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="promotion lineage is contaminated"):
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_omitted_current_semantic_fact_label_use() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
provenance.LineagePurpose.SEMANTIC_FACT,
|
|
|
|
|
case.pack.normalized_content_digest,
|
|
|
|
|
labels_accessed=True,
|
|
|
|
|
arm_id="fact-arm",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert case.custody.current_log(case.population_digest).status is provenance.PopulationStatus.SPENT
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="semantic fact.*protected_population_labels"):
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_cross_arm_protected_semantic_fact_label_use() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
provenance.LineagePurpose.SEMANTIC_FACT,
|
|
|
|
|
case.pack.normalized_content_digest,
|
|
|
|
|
labels_accessed=True,
|
|
|
|
|
arm_id="other-arm",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert case.custody.current_log(case.population_digest).status is provenance.PopulationStatus.SPENT
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="semantic fact.*protected_population_labels"):
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_cross_arm_label_use_from_lineage_source_population() -> None:
|
|
|
|
|
case = _case_with_distinct_evaluation_population()
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
source_population = canonical.content_digest("population")
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
provenance.LineagePurpose.SEMANTIC_FACT,
|
|
|
|
|
case.pack.normalized_content_digest,
|
|
|
|
|
labels_accessed=True,
|
|
|
|
|
arm_id="other-arm",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert source_population != case.population_digest
|
|
|
|
|
assert case.custody.current_log(source_population).status is provenance.PopulationStatus.SPENT
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="semantic fact.*label_access_forbidden"):
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_label_use_from_other_supplied_lineage_population() -> None:
|
|
|
|
|
case, matcher_population = _case_with_three_populations()
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
provenance.LineagePurpose.SEMANTIC_FACT,
|
|
|
|
|
case.pack.normalized_content_digest,
|
|
|
|
|
labels_accessed=True,
|
|
|
|
|
arm_id="other-arm",
|
|
|
|
|
population_digest=matcher_population,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert matcher_population not in {
|
|
|
|
|
case.population_digest,
|
|
|
|
|
case.lineages[0].uses[0].source_population_digest,
|
|
|
|
|
}
|
|
|
|
|
assert case.custody.current_log(matcher_population).status is provenance.PopulationStatus.SPENT
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="semantic fact.*label_access_forbidden"):
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
("purpose", "subject", "labels_accessed"),
|
|
|
|
|
(
|
|
|
|
|
(provenance.LineagePurpose.SEMANTIC_FACT, "exact-pack", False),
|
|
|
|
|
(provenance.LineagePurpose.MATCHING_BEHAVIOR, "exact-pack", True),
|
|
|
|
|
(provenance.LineagePurpose.SEMANTIC_FACT, "wrong-digest", True),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
def test_fact_relation_replay_ignores_unrelated_cross_arm_source_receipts(
|
|
|
|
|
purpose: provenance.LineagePurpose,
|
|
|
|
|
subject: str,
|
|
|
|
|
labels_accessed: bool,
|
|
|
|
|
) -> None:
|
|
|
|
|
case = _case_with_distinct_evaluation_population()
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
influenced = case.pack.normalized_content_digest if subject == "exact-pack" else canonical.content_digest(subject)
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
purpose,
|
|
|
|
|
influenced,
|
|
|
|
|
labels_accessed=labels_accessed,
|
|
|
|
|
arm_id="other-arm",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
("purpose", "subject", "labels_accessed"),
|
|
|
|
|
(
|
|
|
|
|
(provenance.LineagePurpose.SEMANTIC_FACT, "exact-pack", False),
|
|
|
|
|
(provenance.LineagePurpose.MATCHING_BEHAVIOR, "exact-pack", True),
|
|
|
|
|
(provenance.LineagePurpose.SEMANTIC_FACT, "wrong-digest", True),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
def test_fact_relation_replay_ignores_unrelated_receipts_in_other_lineage_population(
|
|
|
|
|
purpose: provenance.LineagePurpose,
|
|
|
|
|
subject: str,
|
|
|
|
|
labels_accessed: bool,
|
|
|
|
|
) -> None:
|
|
|
|
|
case, matcher_population = _case_with_three_populations()
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
influenced = case.pack.normalized_content_digest if subject == "exact-pack" else canonical.content_digest(subject)
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
purpose,
|
|
|
|
|
influenced,
|
|
|
|
|
labels_accessed=labels_accessed,
|
|
|
|
|
arm_id="other-arm",
|
|
|
|
|
population_digest=matcher_population,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
_validate(case, record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fact_relation_replay_refuses_omitted_current_semantic_fact_use_without_labels() -> None:
|
|
|
|
|
case = _case(fact_only=True)
|
|
|
|
|
(relation,) = _apply(case)
|
|
|
|
|
record = _record(case, relation)
|
|
|
|
|
_append_use(
|
|
|
|
|
case.custody,
|
|
|
|
|
case.custody_policy.reviewed_custodian_digests[0],
|
|
|
|
|
provenance.LineagePurpose.SEMANTIC_FACT,
|
|
|
|
|
case.pack.normalized_content_digest,
|
|
|
|
|
labels_accessed=False,
|
|
|
|
|
arm_id="fact-arm",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert case.custody.current_log(case.population_digest).status is provenance.PopulationStatus.AVAILABLE
|
|
|
|
|
with pytest.raises(FactRelationValidationError, match="semantic fact lineage omitted current use"):
|
|
|
|
|
_validate(case, record)
|