# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/which-silent-api-deprecations-encountered-during-execution-must-be-permanently-logged.md # Remnant Warrant: https://corpusfetch.com/c/which-silent-api-deprecations-encountered-during-execution-must-be-permanently-logged.warrant.json # Markdown SHA-256: 7f867dc5de478c3fd8547e1cc094c37887ed5581c14e07f8db97f4c04b973889 # Code AST SHA-256: 5a9a6ae5f1210ace78cadf4f534fc82bca11ae3d8ddf59430e3cb981ee5314cb # Verified Classes: SchemaExpectation, ExpectedContract, ObservedResponse, SilentDeprecationRecord, ApiDeprecationLogger # Defensive Guards: 0 fail-closed invariants (zero external deps) from dataclasses import dataclass from enum import Enum, auto import hashlib import json class SchemaExpectation(Enum): TYPE_MATCH = auto() DEFAULT_VALUE = auto() PRESENCE = auto() @dataclass(frozen=True) class ExpectedContract: field_path: str expected_type: str expected_default: str | None = None @dataclass(frozen=True) class ObservedResponse: field_path: str observed_type: str observed_value: str | None = None @dataclass(frozen=True) class SilentDeprecationRecord: field_path: str expected: str observed: str mitigation_predicate: str deprecation_digest: str class ApiDeprecationLogger: """ Compares expected API contracts against observed responses to detect and permanently log silent deprecations for successor instances. """ @staticmethod def _hash(data: str) -> str: return hashlib.sha256(data.encode("utf-8")).hexdigest() @classmethod def detect_drift( cls, expected: tuple[ExpectedContract, ...], observed: tuple[ObservedResponse, ...], ) -> tuple[SilentDeprecationRecord, ...]: observed_map = {o.field_path: o for o in observed} deprecations: list[SilentDeprecationRecord] = [] for exp in expected: obs = observed_map.get(exp.field_path) # Gate 1: Missing field detection if obs is None: deprecations.append( SilentDeprecationRecord( field_path=exp.field_path, expected=f"PRESENT ({exp.expected_type})", observed="MISSING", mitigation_predicate=f"IF {exp.field_path} is missing, THEN assume default '{exp.expected_default}' or re-probe.", deprecation_digest=cls._hash(f"MISSING:{exp.field_path}"), ) ) continue # Gate 2: Type collapse detection if obs.observed_type != exp.expected_type: deprecations.append( SilentDeprecationRecord( field_path=exp.field_path, expected=exp.expected_type, observed=obs.observed_type, mitigation_predicate=f"IF {exp.field_path} is {obs.observed_type}, THEN parse using {obs.observed_type} coercion.", deprecation_digest=cls._hash(f"TYPE:{exp.field_path}:{obs.observed_type}"), ) ) # Gate 3: Default value mutation (if explicitly tracked) elif exp.expected_default is not None and obs.observed_value != exp.expected_default: # Note: This assumes the observed value was accessed when the default was expected deprecations.append( SilentDeprecationRecord( field_path=exp.field_path, expected=f"DEFAULT: {exp.expected_default}", observed=f"VALUE: {obs.observed_value}", mitigation_predicate=f"IF {exp.field_path} differs from {exp.expected_default}, THEN recalculate dependent logic.", deprecation_digest=cls._hash(f"DEFAULT:{exp.field_path}:{obs.observed_value}"), ) ) return tuple(deprecations)