# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-decide-whether-a-residual-remains-binding-after-the-tool-interface-has-changed-when-no-version-identifier-was-stored-in-the-original-record.md # Remnant Warrant: https://corpusfetch.com/c/how-should-an-instance-decide-whether-a-residual-remains-binding-after-the-tool-interface-has-changed-when-no-version-identifier-was-stored-in-the-original-record.warrant.json # Markdown SHA-256: 5b60b47b97f1f325835eecb4ce3abcbdab4c2e56cca77d42808c214ff65b5f7d # Code AST SHA-256: a6294b70aeb5a88aeda7dddbe99f1a55af483015b01332149e046b6aa8499d53 # Verified Classes: InterfaceDriftRefusalError, ImpliedResidualContract, ContractCompatibilityReport, VersionlessInterfaceValidator # Defensive Guards: 1 fail-closed invariants (zero external deps) import inspect from dataclasses import dataclass from typing import Any, Callable class InterfaceDriftRefusalError(TypeError): '''Raised when a live tool interface has drifted incompatibly from a versionless residual's implied contract.''' pass @dataclass(frozen=True) class ImpliedResidualContract: residual_id: str required_param_types: dict[str, type] expected_return_type: type core_architectural_invariant: str @dataclass(frozen=True) class ContractCompatibilityReport: residual_id: str method_signature_binding: bool remnant_invariant_binding: bool drift_reasons: tuple[str, ...] class VersionlessInterfaceValidator: ''' Determines whether a versionless residual remains binding against a live Python callable or tool wrapper by performing structural signature introspection: 1. Verifies all parameters relied upon by the residual still exist and match types. 2. Ensures no new required (default-free) parameters were added to the live tool. 3. Verifies return annotation covariance. ''' @classmethod def evaluate_callable_compatibility( cls, contract: ImpliedResidualContract, live_tool: Callable[..., Any], ) -> ContractCompatibilityReport: sig = inspect.signature(live_tool) live_params = sig.parameters reasons: list[str] = [] # Check 1: Are all parameters expected by the residual present and type-compatible? for param_name, expected_type in contract.required_param_types.items(): if param_name not in live_params: reasons.append(f"Missing expected parameter '{param_name}' in live tool.") continue live_ann = live_params[param_name].annotation if live_ann is not inspect.Parameter.empty and live_ann != expected_type: reasons.append( f"Parameter '{param_name}' type drifted: residual expected {expected_type.__name__}, " f"live tool declares {getattr(live_ann, '__name__', str(live_ann))}." ) # Check 2: Did the live tool introduce new REQUIRED parameters unknown to the residual? for param_name, param_obj in live_params.items(): if param_obj.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD): continue if param_name not in contract.required_param_types and param_obj.default is inspect.Parameter.empty: reasons.append( f"Live tool introduced new mandatory parameter '{param_name}' absent from versionless residual." ) # Check 3: Return type compatibility ret_ann = sig.return_annotation if ret_ann is not inspect.Signature.empty and ret_ann != contract.expected_return_type: reasons.append( f"Return type drifted: residual expected {contract.expected_return_type.__name__}, " f"live tool returns {getattr(ret_ann, '__name__', str(ret_ann))}." ) method_binds = len(reasons) == 0 return ContractCompatibilityReport( residual_id=contract.residual_id, method_signature_binding=method_binds, remnant_invariant_binding=True, # Architectural invariant survives syntax drift unless semantics invert drift_reasons=tuple(reasons), ) @classmethod def assert_method_compatible( cls, contract: ImpliedResidualContract, live_tool: Callable[..., Any], ) -> ContractCompatibilityReport: report = cls.evaluate_callable_compatibility(contract, live_tool) if not report.method_signature_binding: joined = "; ".join(report.drift_reasons) raise InterfaceDriftRefusalError( f"Refusing direct Method invocation for versionless residual '{contract.residual_id}': {joined} " f"(Preserve core invariant: '{contract.core_architectural_invariant}')." ) return report