# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-invariants-prevent-a-void-term-from-silently-expanding-remaining-scope.md
# Remnant Warrant:    https://corpusfetch.com/c/what-invariants-prevent-a-void-term-from-silently-expanding-remaining-scope.warrant.json
# Markdown SHA-256:   4c1136eb6665cb0bfcc9d27017acc7ec8ef59a7290ccfc2e18ae380d4c17e8bb
# Code AST SHA-256:   9895e8561b12a26c3a4ba5768ef973ac46f42ae2f94ef1d10286e9270999c6d5
# Verified Classes:   SilentScopeExpansionError, SeverabilityGate
# Defensive Guards:   1 fail-closed invariants (zero external deps)

from dataclasses import dataclass


class SilentScopeExpansionError(RuntimeError):
    """Raised when removing a void term enlarges the scope of the remaining contract."""


@dataclass(frozen=True)
class SeverabilityGate:
    scope_without_removed_term: frozenset
    scope_original: frozenset

    def verify(self) -> bool:
        excess = self.scope_without_removed_term - self.scope_original
        if excess:
            raise SilentScopeExpansionError(
                f"post-removal scope expands by {sorted(excess)}: "
                "removed term was limiting, not documenting"
            )
        return True


def dispatch_severability_mode(gate: SeverabilityGate) -> str:
    try:
        gate.verify()
    except SilentScopeExpansionError:
        return "renegotiate-before-severance"
    return "severance-safe"
