feat(detection-core): freeze v1 migration foundation

This commit is contained in:
Marius Ciepluch
2026-11-02 10:23:58 +01:00
parent 1b9018644e
commit a6990e25af
9 changed files with 4087 additions and 1 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
- Existing target facts may be schema-migrated without expanding vocabulary or behavior. - Existing target facts may be schema-migrated without expanding vocabulary or behavior.
- [ ] 1. Establish migration-safe foundations - [ ] 1. Establish migration-safe foundations
- [ ] 1.1 Freeze historical v1 artifacts and the reviewed migration inventory - [x] 1.1 Freeze historical v1 artifacts and the reviewed migration inventory
- Pin representative v1 trace and report bytes and hashes as immutable regression fixtures. - Pin representative v1 trace and report bytes and hashes as immutable regression fixtures.
- Provide an independent schema-routed read-only decoder that validates committed v1 artifacts without importing the compatibility namespace, constructing v2 determinate outcomes, or exposing a writer or converter. - Provide an independent schema-routed read-only decoder that validates committed v1 artifacts without importing the compatibility namespace, constructing v2 determinate outcomes, or exposing a writer or converter.
- Freeze the reviewed initial set of v1 readers and writers with stable entry identities, paths, symbols or capabilities, owner boundaries, and open migration status. - Freeze the reviewed initial set of v1 readers and writers with stable entry identities, paths, symbols or capabilities, owner boundaries, and open migration status.
+328
View File
@@ -0,0 +1,328 @@
"""Read-only validation for committed detection-core v1 artifacts.
The v1 schemas are selected by the caller because historical artifacts do not
self-identify. This module deliberately exposes no writer or v2 conversion.
"""
from __future__ import annotations
import json
import math
from dataclasses import dataclass
from pathlib import Path
from openultrasast.core.canonical import bytes_digest
LEGACY_TRACE_SCHEMA = "trace-v1"
LEGACY_REPORT_SCHEMA = "admission-report-v1"
_TRACE_FIELDS = frozenset(
{
"authority",
"completeness_reasons",
"digests",
"entry",
"guards",
"hops",
"judgment",
"operation_kind",
"resource",
"witnesses",
}
)
_REPORT_FIELDS = frozenset(
{
"coverage",
"criteria_pre_registered",
"decision",
"failed_gates",
"families",
"fix_recall",
"flagged_ordinary_per_100",
"indeterminate_rate",
"instrument_failures",
"manifest",
"pair_correctness",
"pairs",
"precision",
"runtime",
"verified_location_recall",
}
)
_RATE_FIELDS = frozenset({"count", "n", "rate", "wilson95"})
_CRITERIA_FIELDS = frozenset(
{
"max_flagged_ordinary_per_100",
"max_indeterminate_rate",
"min_fix_recall",
"min_pair_correctness",
"min_precision",
"min_verified_location_recall",
}
)
_MANIFEST_FIELDS = frozenset(
{
"acquisition_record_digest",
"analyzer_revision",
"configuration_digest",
"fact_set_digest",
"population_digest",
"population_role",
}
)
class LegacyArtifactError(ValueError):
"""Historical bytes do not satisfy the explicitly selected v1 schema."""
def _reject_duplicate_fields(pairs: list[tuple[str, object]]) -> dict[str, object]:
value: dict[str, object] = {}
for key, item in pairs:
if key in value:
raise LegacyArtifactError(f"duplicate legacy artifact field: {key}")
value[key] = item
return value
def _decode_json(content: bytes) -> object:
try:
return json.loads(content, object_pairs_hook=_reject_duplicate_fields)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise LegacyArtifactError("legacy artifact must be valid UTF-8 JSON") from exc
@dataclass(frozen=True)
class LegacyArtifact:
"""Validated historical bytes, retained without normalization or rewriting."""
schema_version: str
content: bytes
content_digest: str
byte_length: int
@property
def payload(self) -> dict[str, object]:
"""Return a fresh inspection copy of the validated JSON object."""
value = _decode_json(self.content)
if not isinstance(value, dict): # Kept defensive; construction validates this.
raise LegacyArtifactError("legacy artifact must contain a JSON object")
return {str(key): item for key, item in value.items()}
def _object(value: object, fields: frozenset[str], name: str) -> dict[str, object]:
if not isinstance(value, dict) or not all(isinstance(key, str) for key in value):
raise LegacyArtifactError(f"{name} must be an object")
actual = set(value)
if actual != fields:
missing = sorted(fields - actual)
unknown = sorted(actual - fields)
raise LegacyArtifactError(f"{name} has missing fields {missing} and unknown fields {unknown}")
return {str(key): item for key, item in value.items()}
def _text(value: object, name: str) -> str:
if not isinstance(value, str) or not value:
raise LegacyArtifactError(f"{name} must be nonempty text")
return value
def _text_list(value: object, name: str) -> list[str]:
if not isinstance(value, list) or not all(isinstance(item, str) and item for item in value):
raise LegacyArtifactError(f"{name} must be a list of nonempty text")
return value
def _integer(value: object, name: str) -> int:
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
raise LegacyArtifactError(f"{name} must be a non-negative integer")
return value
def _number(value: object, name: str) -> float:
if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value) or value < 0:
raise LegacyArtifactError(f"{name} must be a finite non-negative number")
return float(value)
def _nullable_number(value: object, name: str) -> float | None:
if value is None:
return None
return _number(value, name)
def _objects(value: object, name: str) -> list[object]:
if not isinstance(value, list):
raise LegacyArtifactError(f"{name} must be a list")
return value
def _validate_trace(value: object) -> None:
trace = _object(value, _TRACE_FIELDS, "trace-v1")
for name in ("entry", "operation_kind", "resource", "authority"):
_text(trace[name], name)
if trace["judgment"] not in {"unguarded", "discharged", "indeterminate"}:
raise LegacyArtifactError("judgment is not a recognized v1 value")
_text_list(trace["completeness_reasons"], "completeness_reasons")
_validate_hops(trace["hops"])
_validate_witnesses(trace["witnesses"])
_validate_guards(trace["guards"])
digests = trace["digests"]
if not isinstance(digests, dict) or not all(
isinstance(key, str) and key and isinstance(item, str) and item for key, item in digests.items()
):
raise LegacyArtifactError("digests must map nonempty text to nonempty text")
def _validate_hops(value: object) -> None:
for index, item in enumerate(_objects(value, "hops")):
hop = _object(item, frozenset({"call_line", "complete", "function"}), f"hops[{index}]")
_text(hop["function"], f"hops[{index}].function")
_integer(hop["call_line"], f"hops[{index}].call_line")
if not isinstance(hop["complete"], bool):
raise LegacyArtifactError(f"hops[{index}].complete must be boolean")
def _validate_witnesses(value: object) -> None:
fields = frozenset({"kind", "provenance", "relation"})
for index, item in enumerate(_objects(value, "witnesses")):
witness = _object(item, fields, f"witnesses[{index}]")
for name in fields:
_text(witness[name], f"witnesses[{index}].{name}")
def _validate_guards(value: object) -> None:
fields = frozenset({"kind", "effect"})
for index, item in enumerate(_objects(value, "guards")):
guard = _object(item, fields, f"guards[{index}]")
for name in fields:
_text(guard[name], f"guards[{index}].{name}")
def _validate_report(value: object) -> None:
report = _object(value, _REPORT_FIELDS, "admission-report-v1")
if _number(report["coverage"], "coverage") > 1:
raise LegacyArtifactError("coverage must be between zero and one")
if report["decision"] not in {"GO", "NO-GO"}:
raise LegacyArtifactError("decision is not a recognized v1 value")
failed_gates = _text_list(report["failed_gates"], "failed_gates")
if (report["decision"] == "GO") != (not failed_gates):
raise LegacyArtifactError("decision and failed_gates are inconsistent")
_text_list(report["families"], "families")
_integer(report["instrument_failures"], "instrument_failures")
_validate_criteria(report["criteria_pre_registered"])
_validate_manifest(report["manifest"])
pairs = _validate_pairs(report["pairs"])
rates: dict[str, tuple[int, int]] = {}
for name in (
"fix_recall",
"flagged_ordinary_per_100",
"indeterminate_rate",
"pair_correctness",
"precision",
"verified_location_recall",
):
rates[name] = _validate_rate(report[name], name)
if pairs != rates["pair_correctness"]:
raise LegacyArtifactError("pairs must agree with pair_correctness")
runtime_n = _validate_runtime(report["runtime"])
if runtime_n != pairs[1] or runtime_n != rates["fix_recall"][1]:
raise LegacyArtifactError("runtime.n must agree with the evaluated pair denominator")
def _validate_criteria(value: object) -> None:
criteria = _object(value, _CRITERIA_FIELDS, "criteria_pre_registered")
for name, item in criteria.items():
_number(item, f"criteria_pre_registered.{name}")
def _validate_manifest(value: object) -> None:
manifest = _object(value, _MANIFEST_FIELDS, "manifest")
for name, item in manifest.items():
_text(item, f"manifest.{name}")
def _validate_pairs(value: object) -> tuple[int, int]:
pairs = _object(value, frozenset({"correct", "n"}), "pairs")
correct = _integer(pairs["correct"], "pairs.correct")
total = _integer(pairs["n"], "pairs.n")
if correct > total:
raise LegacyArtifactError("pairs.correct must not exceed pairs.n")
return correct, total
def _validate_runtime(value: object) -> int:
runtime = _object(value, frozenset({"max_seconds", "mean_seconds", "n"}), "runtime")
maximum = _number(runtime["max_seconds"], "runtime.max_seconds")
mean = _number(runtime["mean_seconds"], "runtime.mean_seconds")
if maximum < mean:
raise LegacyArtifactError("runtime.max_seconds must not be below runtime.mean_seconds")
return _integer(runtime["n"], "runtime.n")
def _validate_rate(value: object, name: str) -> tuple[int, int]:
rate = _object(value, _RATE_FIELDS, name)
count = _integer(rate["count"], f"{name}.count")
total = _integer(rate["n"], f"{name}.n")
if count > total:
raise LegacyArtifactError(f"{name}.count must not exceed {name}.n")
actual = _nullable_number(rate["rate"], f"{name}.rate")
interval = rate["wilson95"]
if not isinstance(interval, list) or len(interval) != 2:
raise LegacyArtifactError(f"{name}.wilson95 must contain two bounds")
bounds = tuple(_nullable_number(bound, f"{name}.wilson95[{index}]") for index, bound in enumerate(interval))
if total == 0:
if actual is not None or bounds != (None, None):
raise LegacyArtifactError(f"{name} must use null rate and interval when n is zero")
return count, total
if actual is None or any(bound is None for bound in bounds):
raise LegacyArtifactError(f"{name} must use non-null rate and interval when n is positive")
lower, upper = bounds
assert lower is not None and upper is not None
scale = 100.0 if name == "flagged_ordinary_per_100" else 1.0
expected = count / total * scale
if not math.isclose(actual, expected, rel_tol=1e-12, abs_tol=1e-12):
raise LegacyArtifactError(f"{name}.rate must be derived from count and n")
if not 0 <= lower <= upper <= scale:
raise LegacyArtifactError(f"{name}.wilson95 bounds are invalid")
return count, total
def decode_legacy_artifact(content: bytes, *, schema_version: str) -> LegacyArtifact:
"""Validate exact historical bytes through one explicit v1 schema route."""
if schema_version not in {LEGACY_TRACE_SCHEMA, LEGACY_REPORT_SCHEMA}:
raise LegacyArtifactError(f"unknown legacy schema: {schema_version!r}")
if type(content) is not bytes:
raise LegacyArtifactError("legacy artifact content must be exact bytes")
if not content:
raise LegacyArtifactError("legacy artifact content must be nonempty")
value = _decode_json(content)
if isinstance(value, dict) and "schema_version" in value:
raise LegacyArtifactError("legacy artifact schema is ambiguous; use the explicit route only")
validator = _validate_trace if schema_version == LEGACY_TRACE_SCHEMA else _validate_report
validator(value)
return LegacyArtifact(
schema_version=schema_version,
content=content,
content_digest=bytes_digest(content),
byte_length=len(content),
)
def read_legacy_artifact(path: str | Path, *, schema_version: str) -> LegacyArtifact:
"""Read nonzero historical bytes and validate them without any write path."""
try:
content = Path(path).read_bytes()
except OSError as exc:
raise LegacyArtifactError(f"legacy artifact is not readable: {path}") from exc
return decode_legacy_artifact(content, schema_version=schema_version)
__all__ = [
"LEGACY_REPORT_SCHEMA",
"LEGACY_TRACE_SCHEMA",
"LegacyArtifact",
"LegacyArtifactError",
"decode_legacy_artifact",
"read_legacy_artifact",
]
+187
View File
@@ -0,0 +1,187 @@
"""Independent syntax-only audit of explicit detection-core v1 readers."""
from __future__ import annotations
import ast
import subprocess
from dataclasses import dataclass
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
_EXCLUDED_PREFIXES = ("benchmarks/fixtures/", "benchmarks/pairs/", "tests/fixtures/")
_LEGACY_IMPORTS: dict[str, frozenset[str] | None] = {
"openultrasast.core": frozenset(
{
"AdmissionManifest",
"AdmissionManifestError",
"BoundedSilence",
"Budget",
"Complexity",
"Coverage",
"FactPack",
"Guard",
"Hop",
"InstrumentFailure",
"Judgment",
"ProbeResult",
"ProvenClaim",
"PushRequest",
"PushVerdict",
"TraceRecord",
"Witness",
"admit_fact_pack",
"as_clean",
"check_reads",
"consume_verdict",
"validate_admission_manifest",
}
),
"openultrasast.core._legacy_factpack": None,
"openultrasast.core.admission": frozenset(
{
"AdmissionManifest",
"AdmissionManifestError",
"REPORT_SCHEMA_KEYS",
"validate_admission_manifest",
"validate_report_schema",
}
),
"openultrasast.core.budget": frozenset({"Budget", "Complexity"}),
"openultrasast.core.consumer": None,
"openultrasast.core.coverage": frozenset({"Coverage", "InstrumentFailure", "ResolutionOutcome", "check_reads"}),
"openultrasast.core.factpack": frozenset({"FactPackError", "admit_fact_pack", "admit_pack_file", "load_pack_file", "verify_fact_pack"}),
"openultrasast.core.probes": frozenset(
{
"FittedComponentBlocked",
"LegacyProbeResult",
"ShortcutDetected",
"assert_no_shortcut",
"require_admission_evidence",
"reversed_pair_probe",
}
),
"openultrasast.core.provenance": frozenset({"ProvenanceMismatch", "stamp", "verify_stamp"}),
"openultrasast.core.trace": None,
"openultrasast.core.verdict": None,
}
_TOP_LEVEL_COMPATIBILITY = _LEGACY_IMPORTS["openultrasast.core"] or frozenset()
class LegacyReaderAuditError(ValueError):
"""The explicit-reader audit could not read or parse its declared scope."""
@dataclass(frozen=True)
class ReaderAuditReport:
files_read: int
bytes_read: int
capabilities: tuple[tuple[str, str], ...]
def tracked_python() -> tuple[Path, ...]:
completed = subprocess.run(["git", "ls-files", "*.py"], cwd=REPO, check=True, capture_output=True, text=True)
return tuple(
REPO / relative
for relative in completed.stdout.splitlines()
if relative.endswith(".py") and not relative.startswith(_EXCLUDED_PREFIXES)
)
def _dotted(node: ast.AST) -> str:
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
prefix = _dotted(node.value)
return f"{prefix}.{node.attr}" if prefix else ""
return ""
def _explicit_imports(tree: ast.Module) -> set[str]:
anchors = set()
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module in _LEGACY_IMPORTS:
allowed = _LEGACY_IMPORTS[node.module]
anchors.update(f"import_from:{node.module}:{alias.name}" for alias in node.names if allowed is None or alias.name in allowed)
elif isinstance(node, ast.Import):
anchors.update(
f"import:{alias.name}" for alias in node.names if alias.name in _LEGACY_IMPORTS and _LEGACY_IMPORTS[alias.name] is None
)
return anchors
def _direct_submodule_bindings(tree: ast.Module) -> dict[str, str]:
bindings = {}
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
if alias.name in _LEGACY_IMPORTS and alias.name != "openultrasast.core":
bindings[alias.asname or alias.name] = alias.name
elif isinstance(node, ast.ImportFrom) and node.module == "openultrasast.core":
for alias in node.names:
module = f"openultrasast.core.{alias.name}"
if module in _LEGACY_IMPORTS:
bindings[alias.asname or alias.name] = module
return bindings
def _module_attribute_reads(tree: ast.Module) -> set[str]:
bindings = _direct_submodule_bindings(tree)
reads = set()
for node in ast.walk(tree):
if not isinstance(node, ast.Attribute):
continue
module = bindings.get(_dotted(node.value))
if module is None:
continue
allowed = _LEGACY_IMPORTS[module]
if allowed is None or node.attr in allowed:
reads.add(f"module_attr:{module}:{node.attr}")
return reads
def _top_level_aliases(tree: ast.Module) -> set[str]:
aliases = set()
for node in ast.walk(tree):
if isinstance(node, ast.Import):
aliases.update(alias.asname for alias in node.names if alias.name == "openultrasast.core" and alias.asname)
elif isinstance(node, ast.ImportFrom) and node.module == "openultrasast":
aliases.update(alias.asname or alias.name for alias in node.names if alias.name == "core")
return aliases
def _top_level_reads(tree: ast.Module) -> set[str]:
aliases = _top_level_aliases(tree)
reads = set()
for node in ast.walk(tree):
if not isinstance(node, ast.Attribute) or node.attr not in _TOP_LEVEL_COMPATIBILITY:
continue
base = _dotted(node.value)
if base in aliases or base == "openultrasast.core":
reads.add(f"top_level:{node.attr}")
return reads
def audit_legacy_readers(paths: tuple[Path, ...]) -> ReaderAuditReport:
capabilities = set()
files_read = 0
bytes_read = 0
for path in paths:
relative = path.relative_to(REPO).as_posix()
try:
content = path.read_bytes()
except OSError as exc:
raise LegacyReaderAuditError(f"reader audit input is not readable: {relative}") from exc
if not content:
raise LegacyReaderAuditError(f"reader audit input must contain nonzero bytes: {relative}")
try:
tree = ast.parse(content, filename=relative)
except (SyntaxError, UnicodeDecodeError) as exc:
raise LegacyReaderAuditError(f"reader audit input is not parseable: {relative}") from exc
files_read += 1
bytes_read += len(content)
anchors = _explicit_imports(tree) | _module_attribute_reads(tree) | _top_level_reads(tree)
capabilities.update((relative, anchor) for anchor in anchors)
return ReaderAuditReport(files_read, bytes_read, tuple(sorted(capabilities)))
__all__ = ["LegacyReaderAuditError", "ReaderAuditReport", "audit_legacy_readers", "tracked_python"]
+368
View File
@@ -0,0 +1,368 @@
"""Validation primitives for the frozen detection-core v1 migration worklist."""
from __future__ import annotations
import ast
import hashlib
import json
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from openultrasast.core.legacy import LegacyArtifact, decode_legacy_artifact
REPO = Path(__file__).resolve().parents[1]
INVENTORY_SCHEMA = "openultrasast.detection-core.legacy-migration.v1"
_ENTRY_FIELDS = frozenset(
{
"artifact_family",
"capability_anchors",
"dependency_kind",
"disposition",
"entry_id",
"owner_boundary",
"path",
"symbol",
}
)
_ARTIFACT_FIELDS = frozenset({"byte_length", "path", "schema_version", "sha256"})
_INVENTORY_FIELDS = frozenset({"entries", "historical_artifacts", "schema_version"})
_OWNER_BOUNDARIES = frozenset(
{"authorization-adapter", "benchmark", "core", "hunt", "operational-tooling", "push", "repository-tooling", "tests"}
)
class LegacyInventoryError(ValueError):
"""The frozen migration worklist or one of its targets is invalid."""
class MigrationDisposition(Enum):
OPEN = "open"
V2_MIGRATED = "v2_migrated"
RETIRED = "retired"
class DependencyKind(Enum):
READER = "reader"
WRITER = "writer"
@dataclass(frozen=True)
class HistoricalArtifactRecord:
path: str
schema_version: str
sha256: str
byte_length: int
@dataclass(frozen=True)
class LegacyInventoryEntry:
entry_id: str
path: str
symbol: str
dependency_kind: DependencyKind
artifact_family: str
capability_anchors: tuple[str, ...]
owner_boundary: str
disposition: MigrationDisposition
@dataclass(frozen=True)
class LegacyMigrationInventory:
schema_version: str
historical_artifacts: tuple[HistoricalArtifactRecord, ...]
entries: tuple[LegacyInventoryEntry, ...]
def _object(value: object, fields: frozenset[str], name: str) -> dict[str, object]:
if not isinstance(value, dict) or not all(isinstance(key, str) for key in value):
raise LegacyInventoryError(f"{name} must be an object")
actual = set(value)
if actual != fields:
raise LegacyInventoryError(f"{name} fields differ: missing={sorted(fields - actual)}, unknown={sorted(actual - fields)}")
return value
def _text(value: object, name: str) -> str:
if not isinstance(value, str) or not value:
raise LegacyInventoryError(f"{name} must be nonempty text")
return value
def _sha256(value: object, name: str) -> str:
text = _text(value, name)
if len(text) != 64 or any(character not in "0123456789abcdef" for character in text):
raise LegacyInventoryError(f"{name} must be a lowercase SHA-256 digest")
return text
def _relative_path(value: object, name: str) -> str:
text = _text(value, name)
path = Path(text)
if path.is_absolute() or ".." in path.parts or path.as_posix() != text:
raise LegacyInventoryError(f"{name} must be a normalized repository-relative path")
return text
def _parse_artifact(value: object, index: int) -> HistoricalArtifactRecord:
item = _object(value, _ARTIFACT_FIELDS, f"historical_artifacts[{index}]")
length = item["byte_length"]
if isinstance(length, bool) or not isinstance(length, int) or length <= 0:
raise LegacyInventoryError(f"historical_artifacts[{index}].byte_length must be positive")
return HistoricalArtifactRecord(
path=_relative_path(item["path"], f"historical_artifacts[{index}].path"),
schema_version=_text(item["schema_version"], f"historical_artifacts[{index}].schema_version"),
sha256=_sha256(item["sha256"], f"historical_artifacts[{index}].sha256"),
byte_length=length,
)
def _enum(enum_type: type[Enum], value: object, name: str) -> Enum:
text = _text(value, name)
try:
return enum_type(text)
except ValueError as exc:
raise LegacyInventoryError(f"{name} has unknown value {text!r}") from exc
def _parse_entry(value: object, index: int) -> LegacyInventoryEntry:
item = _object(value, _ENTRY_FIELDS, f"entries[{index}]")
owner = _text(item["owner_boundary"], f"entries[{index}].owner_boundary")
if owner not in _OWNER_BOUNDARIES:
raise LegacyInventoryError(f"entries[{index}].owner_boundary is unknown")
raw_anchors = item["capability_anchors"]
if not isinstance(raw_anchors, list) or not raw_anchors:
raise LegacyInventoryError(f"entries[{index}].capability_anchors must be a nonempty list")
anchors = tuple(_text(anchor, f"entries[{index}].capability_anchors") for anchor in raw_anchors)
if len(set(anchors)) != len(anchors):
raise LegacyInventoryError(f"entries[{index}].capability_anchors contains duplicates")
entry = LegacyInventoryEntry(
entry_id=_sha256(item["entry_id"], f"entries[{index}].entry_id"),
path=_relative_path(item["path"], f"entries[{index}].path"),
symbol=_text(item["symbol"], f"entries[{index}].symbol"),
dependency_kind=_enum(DependencyKind, item["dependency_kind"], f"entries[{index}].dependency_kind"), # type: ignore[arg-type]
artifact_family=_text(item["artifact_family"], f"entries[{index}].artifact_family"),
capability_anchors=anchors,
owner_boundary=owner,
disposition=_enum(MigrationDisposition, item["disposition"], f"entries[{index}].disposition"), # type: ignore[arg-type]
)
if entry.entry_id != entry_identity(entry):
raise LegacyInventoryError(f"entries[{index}].entry_id does not match immutable identity fields")
return entry
def entry_identity(entry: LegacyInventoryEntry) -> str:
"""Derive the stable identity without the mutable migration disposition."""
value = {
"artifact_family": entry.artifact_family,
"capability_anchors": entry.capability_anchors,
"dependency_kind": entry.dependency_kind.value,
"owner_boundary": entry.owner_boundary,
"path": entry.path,
"symbol": entry.symbol,
}
canonical = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
return hashlib.sha256(canonical).hexdigest()
def _decode_inventory(content: bytes) -> LegacyMigrationInventory:
try:
value = json.loads(content)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise LegacyInventoryError("migration inventory must be valid UTF-8 JSON") from exc
root = _object(value, _INVENTORY_FIELDS, "migration inventory")
if root["schema_version"] != INVENTORY_SCHEMA:
raise LegacyInventoryError("migration inventory schema_version is unknown")
raw_artifacts = root["historical_artifacts"]
raw_entries = root["entries"]
if not isinstance(raw_artifacts, list) or not isinstance(raw_entries, list) or not raw_entries:
raise LegacyInventoryError("migration inventory requires artifact and nonempty entry lists")
artifacts = tuple(_parse_artifact(item, index) for index, item in enumerate(raw_artifacts))
entries = tuple(_parse_entry(item, index) for index, item in enumerate(raw_entries))
if len({entry.entry_id for entry in entries}) != len(entries):
raise LegacyInventoryError("migration inventory contains duplicate identities")
return LegacyMigrationInventory(INVENTORY_SCHEMA, artifacts, entries)
def load_migration_inventory(path: str | Path) -> LegacyMigrationInventory:
"""Read the nonzero reviewed worklist without discovering new entries."""
try:
content = Path(path).read_bytes()
except OSError as exc:
raise LegacyInventoryError(f"migration inventory is not readable: {path}") from exc
if not content:
raise LegacyInventoryError("migration inventory must contain nonzero bytes")
return _decode_inventory(content)
def _defined_symbols(tree: ast.Module) -> set[str]:
symbols = {"<module>"}
def visit(nodes: list[ast.stmt], parents: tuple[str, ...] = ()) -> None:
for node in nodes:
if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
current = (*parents, node.name)
symbols.add(".".join(current))
visit(node.body, current)
visit(tree.body)
return symbols
def _call_leaf(node: ast.Call) -> str:
if isinstance(node.func, ast.Name):
return node.func.id
if isinstance(node.func, ast.Attribute):
return node.func.attr
return ""
def _dotted(node: ast.AST) -> str:
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
prefix = _dotted(node.value)
return f"{prefix}.{node.attr}" if prefix else ""
return ""
def _top_level_reads(tree: ast.Module) -> set[str]:
aliases = set()
for node in ast.walk(tree):
if isinstance(node, ast.Import):
aliases.update(alias.asname for alias in node.names if alias.name == "openultrasast.core" and alias.asname)
elif isinstance(node, ast.ImportFrom) and node.module == "openultrasast":
aliases.update(alias.asname or alias.name for alias in node.names if alias.name == "core")
return {
node.attr for node in ast.walk(tree) if isinstance(node, ast.Attribute) and _dotted(node.value) in aliases | {"openultrasast.core"}
}
def _direct_core_module_bindings(tree: ast.Module) -> dict[str, str]:
bindings = {}
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
if alias.name.startswith("openultrasast.core."):
bindings[alias.asname or alias.name] = alias.name
elif isinstance(node, ast.ImportFrom) and node.module == "openultrasast.core":
for alias in node.names:
bindings[alias.asname or alias.name] = f"openultrasast.core.{alias.name}"
return bindings
def _direct_module_attributes(tree: ast.Module) -> set[str]:
bindings = _direct_core_module_bindings(tree)
return {
f"{module}:{node.attr}"
for node in ast.walk(tree)
if isinstance(node, ast.Attribute) and (module := bindings.get(_dotted(node.value))) is not None
}
def _capability_index(tree: ast.Module) -> dict[str, set[str]]:
imported = {
f"{node.module}:{alias.name}"
for node in ast.walk(tree)
if isinstance(node, ast.ImportFrom) and node.module is not None
for alias in node.names
}
modules = {alias.name for node in ast.walk(tree) if isinstance(node, ast.Import) for alias in node.names}
constants = {node.value for node in ast.walk(tree) if isinstance(node, ast.Constant) and isinstance(node.value, str)}
names = {node.id for node in ast.walk(tree) if isinstance(node, ast.Name)}
names.update(node.attr for node in ast.walk(tree) if isinstance(node, ast.Attribute))
return {
"call": {_call_leaf(node) for node in ast.walk(tree) if isinstance(node, ast.Call)},
"constant": constants,
"definition": _defined_symbols(tree),
"import": modules,
"import_from": imported,
"module_attr": _direct_module_attributes(tree),
"name": names,
"top_level": _top_level_reads(tree),
}
def _missing_anchors(entry: LegacyInventoryEntry, tree: ast.Module) -> tuple[str, ...]:
index = _capability_index(tree)
missing = []
for anchor in entry.capability_anchors:
kind, separator, value = anchor.partition(":")
if not separator or kind not in index or value not in index[kind]:
missing.append(anchor)
return tuple(missing)
def validate_inventory_sources(inventory: LegacyMigrationInventory, *, root: Path) -> None:
"""Prove every registered migration target is readable, nonzero, and present."""
for entry in inventory.entries:
path = root / entry.path
try:
content = path.read_bytes()
except OSError as exc:
raise LegacyInventoryError(f"inventory source is not readable: {entry.path}") from exc
if not content:
raise LegacyInventoryError(f"inventory source must contain nonzero bytes: {entry.path}")
try:
tree = ast.parse(content, filename=entry.path)
except (SyntaxError, UnicodeDecodeError) as exc:
raise LegacyInventoryError(f"inventory source is not parseable: {entry.path}") from exc
if entry.symbol not in _defined_symbols(tree):
raise LegacyInventoryError(f"inventory target is absent: {entry.path}:{entry.symbol}")
missing = _missing_anchors(entry, tree)
if missing:
raise LegacyInventoryError(f"inventory capability is absent: {entry.path}:{missing}")
def validate_historical_artifacts(inventory: LegacyMigrationInventory, *, root: Path) -> tuple[LegacyArtifact, ...]:
"""Re-open and validate every byte-frozen v1 artifact from its exact manifest."""
artifacts = []
for record in inventory.historical_artifacts:
try:
content = (root / record.path).read_bytes()
except OSError as exc:
raise LegacyInventoryError(f"historical artifact is not readable: {record.path}") from exc
if len(content) != record.byte_length:
raise LegacyInventoryError(f"historical artifact length changed: {record.path}")
if hashlib.sha256(content).hexdigest() != record.sha256:
raise LegacyInventoryError(f"historical artifact digest changed: {record.path}")
artifact = decode_legacy_artifact(content, schema_version=record.schema_version)
artifacts.append(artifact)
return tuple(artifacts)
def validate_inventory_transition(frozen: LegacyMigrationInventory, candidate: LegacyMigrationInventory) -> None:
"""Allow only disposition closure while retaining the frozen worklist identity."""
frozen_ids = tuple(entry.entry_id for entry in frozen.entries)
candidate_ids = tuple(entry.entry_id for entry in candidate.entries)
if frozen.schema_version != candidate.schema_version or frozen_ids != candidate_ids:
raise LegacyInventoryError("migration inventory identities changed")
if frozen.historical_artifacts != candidate.historical_artifacts:
raise LegacyInventoryError("historical artifact identities changed")
for before, after in zip(frozen.entries, candidate.entries, strict=True):
if not isinstance(before.disposition, MigrationDisposition) or not isinstance(after.disposition, MigrationDisposition):
raise LegacyInventoryError("migration inventory disposition is outside the closed domain")
if not isinstance(before.dependency_kind, DependencyKind) or not isinstance(after.dependency_kind, DependencyKind):
raise LegacyInventoryError("migration inventory dependency kind is outside the closed domain")
if entry_identity(after) != after.entry_id or entry_identity(before) != entry_identity(after):
raise LegacyInventoryError("migration inventory identities changed")
if before.disposition is MigrationDisposition.OPEN:
continue
if after.disposition is not before.disposition:
raise LegacyInventoryError(f"terminal migration entry reopened or changed: {before.entry_id}")
__all__ = [
"REPO",
"DependencyKind",
"HistoricalArtifactRecord",
"LegacyInventoryEntry",
"LegacyInventoryError",
"LegacyMigrationInventory",
"MigrationDisposition",
"entry_identity",
"load_migration_inventory",
"validate_historical_artifacts",
"validate_inventory_sources",
"validate_inventory_transition",
]
File diff suppressed because it is too large Load Diff
+91
View File
@@ -0,0 +1,91 @@
{
"coverage": 0.3333333333333333,
"criteria_pre_registered": {
"max_flagged_ordinary_per_100": 10.0,
"max_indeterminate_rate": 0.5,
"min_fix_recall": 0.5,
"min_pair_correctness": 0.5,
"min_precision": 0.25,
"min_verified_location_recall": 0.5
},
"decision": "NO-GO",
"failed_gates": [
"min_fix_recall: 0.0 < 0.5",
"min_pair_correctness: 0.0 < 0.5",
"min_precision: 0.0 < 0.25",
"min_verified_location_recall: not measured (n=0) vs 0.5"
],
"families": [],
"fix_recall": {
"count": 0,
"n": 12,
"rate": 0.0,
"wilson95": [
0,
0.2425007574992425
]
},
"flagged_ordinary_per_100": {
"count": 2,
"n": 240,
"rate": 0.8333333333333334,
"wilson95": [
0.2288235363161974,
2.9870346273441455
]
},
"indeterminate_rate": {
"count": 3,
"n": 264,
"rate": 0.011363636363636364,
"wilson95": [
0.003871976570054633,
0.03287212890123884
]
},
"instrument_failures": 0,
"manifest": {
"acquisition_record_digest": "none",
"analyzer_revision": "d063167b28ed7db2335737fdb0dae0555225ea85",
"configuration_digest": "095b5a4905ad",
"fact_set_digest": "3eb5bf699434",
"population_digest": "c866c0e812c0b54476c7168fdb0e4a20652de0d30485270afecbe51e5c35e424",
"population_role": "admission"
},
"pair_correctness": {
"count": 0,
"n": 12,
"rate": 0.0,
"wilson95": [
0,
0.2425007574992425
]
},
"pairs": {
"correct": 0,
"n": 12
},
"precision": {
"count": 0,
"n": 14,
"rate": 0.0,
"wilson95": [
0,
0.21531701192718142
]
},
"runtime": {
"max_seconds": 7.112766742007807,
"mean_seconds": 2.8931797610857757,
"n": 12
},
"verified_location_recall": {
"count": 0,
"n": 0,
"rate": null,
"wilson95": [
null,
null
]
}
}
+1
View File
@@ -0,0 +1 @@
{"authority":"admin","completeness_reasons":[],"digests":{"input":"abc123"},"entry":"src/app.py::handler","guards":[{"effect":"branches","kind":"permission_check"}],"hops":[{"call_line":10,"complete":true,"function":"handler"}],"judgment":"unguarded","operation_kind":"protected_read","resource":"credentials","witnesses":[{"kind":"guard","provenance":"fact:checkPermission","relation":"all_paths"}]}
+143
View File
@@ -0,0 +1,143 @@
"""Read-only historical validation for the detection-core v1 migration."""
from __future__ import annotations
import ast
import hashlib
import json
from collections.abc import Callable
from pathlib import Path
import pytest
from openultrasast.core.legacy import (
LEGACY_REPORT_SCHEMA,
LEGACY_TRACE_SCHEMA,
LegacyArtifactError,
decode_legacy_artifact,
read_legacy_artifact,
)
REPO = Path(__file__).resolve().parents[1]
FIXTURES = REPO / "tests/fixtures/detection_core/v1"
TRACE_SHA256 = "93c58ba7c51f11a3173c939e9dcc1eac33334c05a1e066bcc1e4879c4c8de8f0"
REPORT_SHA256 = "e07f10c838b3161040edf05965d1cd1e861023dce36f74f7367dc030b883bcca"
@pytest.mark.parametrize(
("name", "digest", "length"),
(("trace.json", TRACE_SHA256, 401), ("report.json", REPORT_SHA256, 1902)),
)
def test_v1_fixtures_are_nonzero_and_byte_exact(name: str, digest: str, length: int) -> None:
content = (FIXTURES / name).read_bytes()
assert len(content) == length, "the historical instrument must read the expected nonzero bytes"
assert hashlib.sha256(content).hexdigest() == digest
def test_legacy_reader_routes_explicit_trace_and_report_schemas() -> None:
trace = read_legacy_artifact(FIXTURES / "trace.json", schema_version=LEGACY_TRACE_SCHEMA)
report = read_legacy_artifact(FIXTURES / "report.json", schema_version=LEGACY_REPORT_SCHEMA)
assert trace.schema_version == "trace-v1" and trace.byte_length == 401
assert trace.payload["judgment"] == "unguarded"
assert report.schema_version == "admission-report-v1" and report.byte_length == 1902
assert report.payload["decision"] == "NO-GO"
assert trace.content == (FIXTURES / "trace.json").read_bytes()
assert report.content == (FIXTURES / "report.json").read_bytes()
def test_legacy_reader_exposes_no_writer_converter_or_baseline_comparison() -> None:
from openultrasast.core.evidence import EvidenceRecord
artifact = read_legacy_artifact(FIXTURES / "trace.json", schema_version=LEGACY_TRACE_SCHEMA)
assert not isinstance(artifact, EvidenceRecord)
for prohibited in ("write", "to_v2", "decision", "baseline_identity", "compare_identity"):
assert not hasattr(artifact, prohibited)
with pytest.raises(AttributeError):
artifact.content = b"replacement" # type: ignore[misc]
changed = artifact.payload
changed["judgment"] = "discharged"
assert artifact.payload["judgment"] == "unguarded"
@pytest.mark.parametrize("schema", ("", "trace-v2", "admission-report-v2", "unknown"))
def test_legacy_reader_refuses_unknown_or_v2_schema_routes(schema: str) -> None:
content = (FIXTURES / "trace.json").read_bytes()
with pytest.raises(LegacyArtifactError, match="schema"):
decode_legacy_artifact(content, schema_version=schema)
def test_legacy_reader_refuses_cross_routed_or_embedded_schema_identity() -> None:
with pytest.raises(LegacyArtifactError):
decode_legacy_artifact((FIXTURES / "trace.json").read_bytes(), schema_version=LEGACY_REPORT_SCHEMA)
with pytest.raises(LegacyArtifactError):
decode_legacy_artifact((FIXTURES / "report.json").read_bytes(), schema_version=LEGACY_TRACE_SCHEMA)
embedded = {**json.loads((FIXTURES / "trace.json").read_bytes()), "schema_version": LEGACY_TRACE_SCHEMA}
with pytest.raises(LegacyArtifactError, match="ambiguous"):
decode_legacy_artifact(json.dumps(embedded).encode(), schema_version=LEGACY_TRACE_SCHEMA)
def test_legacy_reader_refuses_duplicate_json_fields_and_bytes_subclasses() -> None:
duplicated = b'{"entry":"shadow",' + (FIXTURES / "trace.json").read_bytes()[1:]
with pytest.raises(LegacyArtifactError, match="duplicate"):
decode_legacy_artifact(duplicated, schema_version=LEGACY_TRACE_SCHEMA)
class HostileBytes(bytes):
def __len__(self) -> int:
raise AssertionError("subclass methods must not be observed")
with pytest.raises(LegacyArtifactError, match="exact bytes"):
decode_legacy_artifact(HostileBytes((FIXTURES / "trace.json").read_bytes()), schema_version=LEGACY_TRACE_SCHEMA)
@pytest.mark.parametrize(
("fixture", "schema", "mutation"),
(
("trace.json", "trace-v1", lambda value: {**value, "unknown": True}),
("trace.json", "trace-v1", lambda value: {**value, "judgment": "clean"}),
("report.json", "admission-report-v1", lambda value: {**value, "decision": "MAYBE"}),
("report.json", "admission-report-v1", lambda value: {key: item for key, item in value.items() if key != "manifest"}),
),
)
def test_legacy_reader_refuses_open_or_invalid_historical_shapes(
fixture: str,
schema: str,
mutation: Callable[[dict[str, object]], dict[str, object]],
) -> None:
value = json.loads((FIXTURES / fixture).read_bytes())
with pytest.raises(LegacyArtifactError):
decode_legacy_artifact(json.dumps(mutation(value)).encode(), schema_version=schema)
def test_legacy_reader_refuses_empty_or_unreadable_input(tmp_path: Path) -> None:
empty = tmp_path / "empty.json"
empty.write_bytes(b"")
with pytest.raises(LegacyArtifactError, match="nonempty"):
read_legacy_artifact(empty, schema_version=LEGACY_TRACE_SCHEMA)
with pytest.raises(LegacyArtifactError, match="readable"):
read_legacy_artifact(tmp_path / "missing.json", schema_version=LEGACY_TRACE_SCHEMA)
def test_legacy_decoder_has_no_compatibility_or_v2_domain_dependency() -> None:
path = REPO / "src/openultrasast/core/legacy.py"
content = path.read_bytes()
assert content, "the import-boundary instrument must read nonzero decoder bytes"
tree = ast.parse(content, filename=str(path.relative_to(REPO)))
modules = {node.module for node in ast.walk(tree) if isinstance(node, ast.ImportFrom) and node.module is not None} | {
alias.name for node in ast.walk(tree) if isinstance(node, ast.Import) for alias in node.names
}
assert "openultrasast.core.legacy_v1" not in modules
assert "openultrasast.core.evidence" not in modules
assert modules & {"openultrasast.core.canonical"} == {"openultrasast.core.canonical"}
def test_legacy_reader_is_not_reexported_from_the_public_core_api() -> None:
import openultrasast.core as core
assert not ({"LegacyArtifact", "decode_legacy_artifact", "read_legacy_artifact"} & set(core.__all__))
+293
View File
@@ -0,0 +1,293 @@
"""Immutable worklist tests for the detection-core v1 migration."""
from __future__ import annotations
import json
from dataclasses import replace
from pathlib import Path
import core_migration_audit
import pytest
from core_migration_audit import audit_legacy_readers, tracked_python
from core_migration_inventory import (
REPO,
LegacyInventoryError,
MigrationDisposition,
entry_identity,
load_migration_inventory,
validate_historical_artifacts,
validate_inventory_sources,
validate_inventory_transition,
)
INVENTORY = REPO / "tests/fixtures/detection_core/v1/migration_inventory.json"
@pytest.mark.parametrize(
("source", "expected"),
(
(
"import openultrasast.core.probes as legacy_probes\nresult = legacy_probes.LegacyProbeResult\n",
"module_attr:openultrasast.core.probes:LegacyProbeResult",
),
(
"from openultrasast.core import budget as legacy_budget\nresult = legacy_budget.Budget\n",
"module_attr:openultrasast.core.budget:Budget",
),
),
)
def test_reader_audit_covers_direct_legacy_submodule_bindings(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, source: str, expected: str
) -> None:
monkeypatch.setattr(core_migration_audit, "REPO", tmp_path)
path = tmp_path / "reader.py"
path.write_text(source, encoding="utf-8")
report = audit_legacy_readers((path,))
assert report.files_read == 1 and report.bytes_read == len(source.encode())
assert report.capabilities == (("reader.py", expected),)
def test_reader_audit_covers_real_direct_legacy_submodule_reads() -> None:
expected = {
("tests/test_core_budget_v2.py", "module_attr:openultrasast.core.budget:Budget"),
("tests/test_core_budget_v2.py", "module_attr:openultrasast.core.budget:Complexity"),
("tests/test_core_probes.py", "module_attr:openultrasast.core.probes:FittedComponentBlocked"),
("tests/test_core_probes.py", "module_attr:openultrasast.core.probes:LegacyProbeResult"),
("tests/test_core_probes.py", "module_attr:openultrasast.core.probes:ShortcutDetected"),
("tests/test_core_probes.py", "module_attr:openultrasast.core.probes:assert_no_shortcut"),
("tests/test_core_probes.py", "module_attr:openultrasast.core.probes:require_admission_evidence"),
("tests/test_core_probes.py", "module_attr:openultrasast.core.probes:reversed_pair_probe"),
("tests/test_core_contract.py", "module_attr:openultrasast.core.verdict:__all__"),
}
paths = tuple(REPO / path for path in sorted({path for path, _ in expected}))
report = audit_legacy_readers(paths)
assert report.files_read == 3 and report.bytes_read > 0
assert expected <= set(report.capabilities)
def test_frozen_inventory_resolves_nonzero_sources_and_targets() -> None:
content = INVENTORY.read_bytes()
assert content, "the inventory instrument must read nonzero bytes"
inventory = load_migration_inventory(INVENTORY)
assert inventory.entries
assert all(entry.disposition is MigrationDisposition.OPEN for entry in inventory.entries)
assert all(entry.entry_id == entry_identity(entry) for entry in inventory.entries)
validate_inventory_sources(inventory, root=REPO)
def test_inventory_covers_known_live_legacy_boundaries() -> None:
inventory = load_migration_inventory(INVENTORY)
registered_readers = {entry.path for entry in inventory.entries if entry.dependency_kind.value == "reader"}
registered_writers = {entry.path for entry in inventory.entries if entry.dependency_kind.value == "writer"}
assert registered_readers == {
"benchmarks/valid/arm_regression.py",
"benchmarks/valid/prover_admission.py",
"src/openultrasast/core/__init__.py",
"src/openultrasast/core/consumer.py",
"src/openultrasast/core/factpack.py",
"src/openultrasast/core/trace.py",
"src/openultrasast/hunt/prover.py",
"src/openultrasast/semantic/obligations/core_adapter.py",
"tests/test_core_admission.py",
"tests/test_core_budget.py",
"tests/test_core_budget_v2.py",
"tests/test_core_contract.py",
"tests/test_core_coverage_v2.py",
"tests/test_core_factpack_consumer.py",
"tests/test_core_factpack_file.py",
"tests/test_core_native.py",
"tests/test_core_probes.py",
"tests/test_core_validation.py",
}
assert registered_writers == {
"benchmarks/learn/engine_task_entry.py",
"benchmarks/measurements/2026-10-14-access-control-hunt/analyse.py",
"benchmarks/unseen/calibrate.py",
"benchmarks/unseen/replay.py",
"benchmarks/unseen/score.py",
"benchmarks/valid/admission_run.py",
"benchmarks/valid/arm_regression.py",
"benchmarks/valid/development_runner.py",
"benchmarks/valid/hunter_arm.py",
"benchmarks/valid/judge_pass.py",
"benchmarks/valid/metrics.py",
"benchmarks/valid/prover_arm.py",
"benchmarks/valid/semgrep_arm.py",
"ops/smoke_admission.py",
"ops/smoke_report.py",
"src/openultrasast/core/_legacy_factpack.py",
"src/openultrasast/core/consumer.py",
"src/openultrasast/core/probes.py",
"src/openultrasast/core/trace.py",
"src/openultrasast/hunt/prover.py",
"src/openultrasast/hunt/report.py",
"src/openultrasast/hunt/runner.py",
"src/openultrasast/push/runner.py",
"src/openultrasast/semantic/obligations/core_adapter.py",
"tests/test_admission_scoring.py",
"tests/test_arm_regression.py",
"tests/test_core_contract.py",
"tests/test_core_factpack_consumer.py",
"tests/test_development_runner.py",
"tests/test_four_arm.py",
"tests/test_hunt_ablation.py",
"tests/test_hunt_artifact.py",
"tests/test_hunt_contamination.py",
"tests/test_hunter_arm.py",
"tests/test_obligation_judgment.py",
"tests/test_obligation_trace.py",
"tests/test_push_admission.py",
"tests/test_push_report.py",
"tests/test_runner_gates.py",
"tests/test_semgrep_arm.py",
"tests/test_unseen_calibrate.py",
"tests/test_unseen_proof.py",
"tests/test_unseen_replay.py",
"tests/test_unseen_score.py",
}
def test_inventory_registers_the_active_legacy_probe_writer() -> None:
inventory = load_migration_inventory(INVENTORY)
assert any(
entry.path == "src/openultrasast/core/probes.py"
and entry.symbol == "reversed_pair_probe"
and entry.dependency_kind.value == "writer"
for entry in inventory.entries
)
def test_independent_reader_audit_matches_split_inventory_capabilities() -> None:
report = audit_legacy_readers(tracked_python())
inventory = load_migration_inventory(INVENTORY)
reader_entries = tuple(entry for entry in inventory.entries if entry.dependency_kind.value == "reader")
registered = {(entry.path, anchor) for entry in reader_entries for anchor in entry.capability_anchors}
assert report.files_read > 0 and report.bytes_read > 0
assert ("tests/test_core_probes.py", "top_level:ProbeResult") in report.capabilities
assert all(len(entry.capability_anchors) == 1 for entry in reader_entries)
assert registered == set(report.capabilities)
def test_inventory_targets_require_exact_capability_anchors(tmp_path: Path) -> None:
frozen = load_migration_inventory(INVENTORY)
entry = frozen.entries[0]
assert entry.capability_anchors
substituted = tmp_path / entry.path
substituted.parent.mkdir(parents=True)
substituted.write_text("def unrelated():\n return True\n", encoding="utf-8")
with pytest.raises(LegacyInventoryError, match="capability"):
validate_inventory_sources(replace(frozen, entries=(entry,)), root=tmp_path)
def test_historical_artifact_manifest_pins_exact_nonzero_bytes() -> None:
inventory = load_migration_inventory(INVENTORY)
artifacts = validate_historical_artifacts(inventory, root=REPO)
assert {artifact.schema_version for artifact in artifacts} == {"trace-v1", "admission-report-v1"}
assert all(artifact.byte_length > 0 for artifact in artifacts)
@pytest.mark.parametrize("terminal", (MigrationDisposition.V2_MIGRATED, MigrationDisposition.RETIRED))
def test_inventory_entries_close_monotonically_without_identity_changes(terminal: MigrationDisposition) -> None:
frozen = load_migration_inventory(INVENTORY)
changed_entry = replace(frozen.entries[0], disposition=terminal)
candidate = replace(frozen, entries=(changed_entry, *frozen.entries[1:]))
validate_inventory_transition(frozen, candidate)
with pytest.raises(LegacyInventoryError, match="terminal"):
validate_inventory_transition(candidate, frozen)
def test_inventory_transition_refuses_runtime_invalid_disposition() -> None:
frozen = load_migration_inventory(INVENTORY)
invalid = replace(frozen.entries[0], disposition="closed") # type: ignore[arg-type]
candidate = replace(frozen, entries=(invalid, *frozen.entries[1:]))
with pytest.raises(LegacyInventoryError, match="disposition"):
validate_inventory_transition(frozen, candidate)
@pytest.mark.parametrize("mutation", ("add", "delete", "rekey"))
def test_inventory_refuses_additions_deletions_and_rekeying(mutation: str) -> None:
frozen = load_migration_inventory(INVENTORY)
if mutation == "add":
added = replace(
frozen.entries[0],
entry_id="f" * 64,
path="src/openultrasast/core/canonical.py",
symbol="<module>",
)
candidate = replace(frozen, entries=(*frozen.entries, added))
elif mutation == "delete":
candidate = replace(frozen, entries=frozen.entries[1:])
else:
changed = replace(frozen.entries[0], symbol="different_target")
changed = replace(changed, entry_id=entry_identity(changed))
candidate = replace(frozen, entries=(changed, *frozen.entries[1:]))
with pytest.raises(LegacyInventoryError, match="identities"):
validate_inventory_transition(frozen, candidate)
@pytest.mark.parametrize(
("field", "value", "message"),
(("schema_version", "v2", "schema"), ("disposition", "closed", "disposition")),
)
def test_inventory_refuses_schema_and_status_mutations(tmp_path: Path, field: str, value: str, message: str) -> None:
document = json.loads(INVENTORY.read_bytes())
if field == "schema_version":
document[field] = value
else:
document["entries"][0][field] = value
changed = tmp_path / "migration_inventory.json"
changed.write_text(json.dumps(document), encoding="utf-8")
with pytest.raises(LegacyInventoryError, match=message):
load_migration_inventory(changed)
def test_inventory_refuses_unreadable_zero_and_unknown_targets(tmp_path: Path) -> None:
frozen = load_migration_inventory(INVENTORY)
original = frozen.entries[0]
missing = replace(original, path="missing.py", symbol="<module>")
missing = replace(missing, entry_id=entry_identity(missing))
with pytest.raises(LegacyInventoryError, match="readable"):
validate_inventory_sources(replace(frozen, entries=(missing,)), root=tmp_path)
empty_path = tmp_path / "empty.py"
empty_path.write_bytes(b"")
empty = replace(original, path="empty.py", symbol="<module>")
empty = replace(empty, entry_id=entry_identity(empty))
with pytest.raises(LegacyInventoryError, match="nonzero"):
validate_inventory_sources(replace(frozen, entries=(empty,)), root=tmp_path)
source_path = tmp_path / "source.py"
source_path.write_text("def present():\n return True\n", encoding="utf-8")
unknown = replace(original, path="source.py", symbol="absent")
unknown = replace(unknown, entry_id=entry_identity(unknown))
with pytest.raises(LegacyInventoryError, match="target"):
validate_inventory_sources(replace(frozen, entries=(unknown,)), root=tmp_path)
def test_inventory_refuses_changed_artifact_bytes(tmp_path: Path) -> None:
frozen = load_migration_inventory(INVENTORY)
artifact = frozen.historical_artifacts[0]
target = tmp_path / artifact.path
target.parent.mkdir(parents=True)
target.write_bytes(b"{}\n")
with pytest.raises(LegacyInventoryError, match="digest|length"):
validate_historical_artifacts(replace(frozen, historical_artifacts=(artifact,)), root=tmp_path)