# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/what-invariants-prevent-tool-discovery-caches-from-returning-deprecated-tool-versions.md # Remnant Warrant: https://corpusfetch.com/c/what-invariants-prevent-tool-discovery-caches-from-returning-deprecated-tool-versions.warrant.json # Markdown SHA-256: 7fe0c69c9683adfc75060ea37111487c215516aa955f032a38004d19b773aa75 # Code AST SHA-256: 6a448574edcbfa469e8dcbf0d43f0da2c83e635f05085012fe3ed7f59fe2d956 # Verified Classes: CacheVerdict, ToolCacheEntry, CachePolicy, CacheManifest, ToolDiscoveryCacheGuard # Defensive Guards: 0 fail-closed invariants (zero external deps) from dataclasses import dataclass from enum import Enum, auto import hashlib import json class CacheVerdict(Enum): LIVE_LOOKUP = auto() CACHE_FRESH = auto() CACHE_STALE_VERSION = auto() DEPRECATION_PROPAGATION_PENDING = auto() @dataclass(frozen=True) class ToolCacheEntry: tool_id: str cached_version: str current_version: str cache_age_sec: float deprecation_pending: bool propagation_witness_present: bool @dataclass(frozen=True) class CachePolicy: max_cache_age_sec: float require_propagation_witness: bool @dataclass(frozen=True) class CacheManifest: verdict: CacheVerdict directive: str manifest_digest: str class ToolDiscoveryCacheGuard: @staticmethod def _hash(data: str) -> str: return hashlib.sha256(data.encode("utf-8")).hexdigest() @classmethod def evaluate_cache_entry( cls, entry: ToolCacheEntry, policy: CachePolicy ) -> CacheManifest: # Gate 1: Version mismatch if entry.cached_version != entry.current_version: return CacheManifest( verdict=CacheVerdict.CACHE_STALE_VERSION, directive=f"HALT: Cached version {entry.cached_version} != current {entry.current_version}. Re-fetch.", manifest_digest=cls._hash(f"VERSION_DRIFT:{entry.tool_id}") ) # Gate 2: Deprecation pending if entry.deprecation_pending: return CacheManifest( verdict=CacheVerdict.DEPRECATION_PROPAGATION_PENDING, directive=f"WAIT: Deprecation pending for {entry.tool_id}. Invalidate and re-fetch.", manifest_digest=cls._hash(f"DEPRECATION:{entry.tool_id}") ) # Gate 3: Cache age if entry.cache_age_sec > policy.max_cache_age_sec: return CacheManifest( verdict=CacheVerdict.CACHE_STALE_VERSION, directive=f"HALT: Cache age {entry.cache_age_sec:.0f}s > {policy.max_cache_age_sec:.0f}s. Re-fetch.", manifest_digest=cls._hash(f"AGE:{entry.cache_age_sec:.0f}") ) # Gate 4: Propagation witness if policy.require_propagation_witness and not entry.propagation_witness_present: return CacheManifest( verdict=CacheVerdict.DEPRECATION_PROPAGATION_PENDING, directive=f"HALT: No propagation witness for {entry.tool_id}. Cannot confirm deprecation-free.", manifest_digest=cls._hash(f"NO_WITNESS:{entry.tool_id}") ) # Gate 5: Fresh digest = cls._hash(json.dumps({ "tool": entry.tool_id, "version": entry.current_version, "age": f"{entry.cache_age_sec:.0f}", "witness": str(entry.propagation_witness_present) }, sort_keys=True)) return CacheManifest( verdict=CacheVerdict.CACHE_FRESH, directive=f"PROCEED: Cached version {entry.current_version} fresh and non-deprecated.", manifest_digest=digest )