# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/what-federated-learning-boundary-invariants-must-survive-when-agents-contribute-training-signals.md # Remnant Warrant: https://corpusfetch.com/c/what-federated-learning-boundary-invariants-must-survive-when-agents-contribute-training-signals.warrant.json # Markdown SHA-256: c24eb3d59fbe676ae881d644bf6aba06964e973885cb692c29c64cf862d428ed # Code AST SHA-256: 09022dbf32d28fe6b9358ec1a8cc60e4ad1db26c034abbbf5c5e22f5cb53df71 # Verified Classes: FederatedVerdict, Contribution, FederatedState, FederatedPolicy, FederatedManifest, FederatedBoundaryGuard # Defensive Guards: 0 fail-closed invariants (zero external deps) from dataclasses import dataclass from enum import Enum, auto import hashlib import json class FederatedVerdict(Enum): NO_SENSITIVE_CONTRIBUTION = auto() PRIVACY_PRESERVING_CONTRIBUTION = auto() PRIVACY_REQUIRED = auto() ATTRIBUTION_BOUNDARY_VIOLATED = auto() @dataclass(frozen=True) class Contribution: contributor_id: str contribution_hash: str privacy_receipt: str | None contains_sensitive_data: bool @dataclass(frozen=True) class FederatedState: contributions: tuple[Contribution, ...] aggregation_anonymized: bool min_contribution_threshold: int @dataclass(frozen=True) class FederatedPolicy: require_dp_or_secure_agg: bool require_min_contributors: bool @dataclass(frozen=True) class FederatedManifest: verdict: FederatedVerdict directive: str manifest_digest: str class FederatedBoundaryGuard: @staticmethod def _hash(data: str) -> str: return hashlib.sha256(data.encode("utf-8")).hexdigest() @classmethod def evaluate_contribution( cls, state: FederatedState, policy: FederatedPolicy ) -> FederatedManifest: # Gate 1: No sensitive data if not any(c.contains_sensitive_data for c in state.contributions): digest = cls._hash(json.dumps({ "contributions": str(len(state.contributions)), "sensitivity": "NONE" }, sort_keys=True)) return FederatedManifest( verdict=FederatedVerdict.NO_SENSITIVE_CONTRIBUTION, directive="PROCEED: No sensitive data in contributions.", manifest_digest=digest ) # Gate 2: Privacy receipt if policy.require_dp_or_secure_agg: missing_privacy = [ c.contributor_id for c in state.contributions if c.contains_sensitive_data and not c.privacy_receipt ] if missing_privacy: return FederatedManifest( verdict=FederatedVerdict.PRIVACY_REQUIRED, directive=f"HALT: {len(missing_privacy)} sensitive contributions lack privacy receipt.", manifest_digest=cls._hash(f"NO_PRIVACY:{len(missing_privacy)}") ) # Gate 3: Minimum contributors if policy.require_min_contributors: if len(state.contributions) < state.min_contribution_threshold: return FederatedManifest( verdict=FederatedVerdict.ATTRIBUTION_BOUNDARY_VIOLATED, directive=f"HALT: {len(state.contributions)} contributors < {state.min_contribution_threshold}. Attribution possible.", manifest_digest=cls._hash(f"FEW_CONTRIBUTORS:{len(state.contributions)}") ) # Gate 4: Aggregation anonymized if not state.aggregation_anonymized: return FederatedManifest( verdict=FederatedVerdict.ATTRIBUTION_BOUNDARY_VIOLATED, directive="HALT: Aggregation not anonymized. Individual contributions isolated.", manifest_digest=cls._hash("NOT_ANONYMIZED") ) # Gate 5: Privacy-preserving digest = cls._hash(json.dumps({ "contributions": str(len(state.contributions)), "all_private": "TRUE", "anonymized": str(state.aggregation_anonymized), "min_contributors": str(state.min_contribution_threshold) }, sort_keys=True)) return FederatedManifest( verdict=FederatedVerdict.PRIVACY_PRESERVING_CONTRIBUTION, directive=f"PROCEED: {len(state.contributions)} contributions with privacy receipts and anonymized aggregation.", manifest_digest=digest )