feat(pre-push-safety-net): key evidence reuse by compatible semantics

This commit is contained in:
Marius Ciepluch
2026-09-13 16:16:19 +02:00
parent 6fb5ad2cff
commit 0ab5d56618
6 changed files with 276 additions and 4 deletions
+54 -1
View File
@@ -12,10 +12,11 @@ import tempfile
import time
from collections.abc import Iterator
from contextlib import ExitStack, contextmanager
from dataclasses import dataclass
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any
from openultrasast.contracts import Contract
from openultrasast.cpg.artifact import digest_value, graph_bytes, read_bytes
from openultrasast.cpg.backend import _DeadlineExpired, _remove_owned_tree
from openultrasast.model.contracts import ExecutionBudget
@@ -123,6 +124,24 @@ class ArtifactCache:
self.hits += 1
yield entry
def get_json(self, key: str, *, kind: str, budget: ExecutionBudget) -> Any:
with self.lookup(key, budget) as entry:
if entry is None or entry.metadata != {"kind": kind} or entry.path.stat().st_size > 32 * 1024**2:
return None
try:
return json.loads(read_bytes(entry.path, budget.deadline_monotonic))
except (OSError, ValueError, TimeoutError):
return None
def put_json(self, key: str, payload: object, *, kind: str, budget: ExecutionBudget, complete: bool) -> bool:
if not complete or time.monotonic() >= budget.deadline_monotonic:
return False
try:
data = json.dumps(payload, sort_keys=True, allow_nan=False).encode()
except (TypeError, ValueError):
return False
return self.publish(key, data, {"kind": kind}, budget, complete=complete)
def _remove(self, root: Path, budget: ExecutionBudget) -> None:
_remove_owned_tree(root, budget.deadline_monotonic)
@@ -215,3 +234,37 @@ class ArtifactCache:
_remove_owned_tree(pending, budget.deadline_monotonic + budget.cancellation_allowance_seconds)
except (OSError, _DeadlineExpired):
self.last_reason = "cache_partial_cleanup_incomplete"
@dataclass(frozen=True)
class SemanticKeys(Contract):
facts: str
queries: str
configuration: str
ranking: str
admission: str
model: str
eligibility: str
mode: str
def query(self, *, graph: str, kind: str, request: object, context: object) -> str:
return digest_value(
{
"layer": "query-v1",
"graph": graph,
"kind": kind,
"request": request,
"context": context,
"facts": self.facts,
"queries": self.queries,
"configuration": self.configuration,
}
)
def comparison(self, *, base: str, head: str, context: object, evidence: object) -> str:
return digest_value(
{"layer": "comparison-v1", "base": base, "head": head, "context": context, "evidence": evidence, "semantics": asdict(self)}
)
def result(self, *, comparisons: object, scope: object) -> str:
return digest_value({"layer": "result-v1", "comparisons": comparisons, "scope": scope, "semantics": asdict(self)})
+98 -2
View File
@@ -6,7 +6,7 @@ import hashlib
import json
import time
from collections.abc import Sequence
from dataclasses import dataclass, replace
from dataclasses import asdict, dataclass, replace
from pathlib import Path, PurePosixPath
from string import Formatter
from typing import Any, Literal
@@ -20,6 +20,7 @@ from openultrasast.model.pipeline import ModelFinding, _arbitrate_all
from openultrasast.model.regions import ScanRegion
from openultrasast.model.scan import ModelScanResult, ScanBudget, _prefetched, _spec_for, scan_repository
from openultrasast.model.specs import ConfigSpec, DominanceSpec, TaintSpec
from openultrasast.push.cache import ArtifactCache, SemanticKeys
from openultrasast.push.contracts import ComparisonAnalysis, PushComparison, PushResult
@@ -247,6 +248,92 @@ def compare_evidence(
head_semantics: str,
base_semantics: str,
execution_budget: ExecutionBudget | None = None,
cache: ArtifactCache | None = None,
cache_semantics: SemanticKeys | None = None,
) -> tuple[CandidateDelta, ...]:
"""Reuse completed comparison evidence; alert eligibility is always applied later."""
def complete(scan: ModelScanResult | None) -> bool:
return bool(
scan
and scan.scope
and scan.scope.population_complete
and not scan.degradations
and not scan.scope.unresolved_boundaries
and all(item.reason == "tier_zero" for item in scan.scope.deferred)
and all(item.status == "completed" for item in scan.question_outcomes)
and {item.identity for item in scan.scope.selected} == {item.identity for item in scan.question_outcomes}
)
key = None
if (
cache is not None
and cache_semantics is not None
and execution_budget is not None
and time.monotonic() < execution_budget.deadline_monotonic
and context.base_revision is not None
and not context.unresolved_boundaries
and head_semantics == base_semantics
and complete(head)
and complete(base)
):
from openultrasast.cpg.artifact import read_bytes
def evidence(scan: ModelScanResult) -> dict[str, object]:
return {
name: value
for name, value in asdict(scan).items()
if name not in {"seconds", "build_seconds", "query_seconds", "query_seconds_by_kind"}
}
assert base is not None
try:
key = cache_semantics.comparison(
base=context.base_revision,
head=context.head_revision,
context=context.to_payload(),
evidence={
"head": evidence(head),
"base": evidence(base),
"semantics": head_semantics,
"policy": hashlib.sha256(read_bytes(Path(__file__), execution_budget.deadline_monotonic)).hexdigest(),
},
)
raw = cache.get_json(key, kind="comparison", budget=execution_budget)
if isinstance(raw, list):
decoded = tuple(CandidateDelta.from_payload(item) for item in raw)
if time.monotonic() < execution_budget.deadline_monotonic and all(
item.novelty != "unknown"
and item.base_revision == context.base_revision
and item.head_revision == context.head_revision
and item.semantics == head_semantics
for item in decoded
):
return decoded
except (OSError, ValueError, TimeoutError):
key = None
result = _compare_evidence(
head, base, context=context, head_semantics=head_semantics, base_semantics=base_semantics, execution_budget=execution_budget
)
if key is not None and cache is not None and execution_budget is not None:
cache.put_json(
key,
[item.to_payload() for item in result],
kind="comparison",
budget=execution_budget,
complete=all(item.novelty != "unknown" for item in result),
)
return result
def _compare_evidence(
head: ModelScanResult,
base: ModelScanResult | None,
*,
context: ChangeContext,
head_semantics: str,
base_semantics: str,
execution_budget: ExecutionBudget | None = None,
) -> tuple[CandidateDelta, ...]:
"""Absence counts only in completed comparable scope, never from scan silence."""
head_ops, base_ops = _operations(head, execution_budget), _operations(base, execution_budget) if base else ()
@@ -356,6 +443,8 @@ def compare_targeted_base(
scan_budget: ScanBudget,
head_semantics: str,
base_semantics: str,
cache: ArtifactCache | None = None,
cache_semantics: SemanticKeys | None = None,
) -> DeltaComparison:
"""Re-run counterparts of the ranker's selected head questions through the existing driver.
@@ -398,7 +487,14 @@ def compare_targeted_base(
except Exception as error: # noqa: BLE001 -- comparison failure is coverage, not a new defect
reasons.append(f"base_scan_failed:{type(error).__name__}")
candidates = compare_evidence(
head, base, context=context, head_semantics=head_semantics, base_semantics=base_semantics, execution_budget=execution_budget
head,
base,
context=context,
head_semantics=head_semantics,
base_semantics=base_semantics,
execution_budget=execution_budget,
cache=cache,
cache_semantics=cache_semantics,
)
reasons.extend(c.reason for c in candidates if c.novelty == "unknown")
return DeltaComparison(candidates, base, head_semantics, tuple(dict.fromkeys(reasons)))