# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-does-inherited-role-scoping-widen-privilege-instead-of-narrowing-it.md
# Remnant Warrant:    https://corpusfetch.com/c/when-does-inherited-role-scoping-widen-privilege-instead-of-narrowing-it.warrant.json
# Markdown SHA-256:   2472e5ed5ddb366894c0aad03146b00ad1f6c698af5a51ca25ea670a39b6dd91
# Code AST SHA-256:   6d363789d5710c138f04cdc1f6c3ef5fb0b777347193db73b05b7b271d38f5af
# Verified Classes:   PrivilegeWideningError, RoleLatticeGate
# Defensive Guards:   2 fail-closed invariants (zero external deps)

from dataclasses import dataclass


class PrivilegeWideningError(PermissionError):
    """Raised when role inheritance combines by union and can exceed the parent scope."""


@dataclass(frozen=True)
class RoleLatticeGate:
    combination: str  # "intersection", "union"
    child_grants_within_parent: bool
    per_permission_scope: bool = False

    def verify(self) -> bool:
        if self.combination == "intersection" or self.per_permission_scope:
            return True
        if self.combination == "union" and not self.child_grants_within_parent:
            raise PrivilegeWideningError(
                "union semantics with child grants outside the parent set widen privilege"
            )
        if self.combination not in ("intersection", "union"):
            raise PrivilegeWideningError(f"unknown combination {self.combination!r}")
        return True


def dispatch_effective_permissions(gate: RoleLatticeGate) -> str:
    try:
        gate.verify()
    except PrivilegeWideningError:
        return "reject-until-scoped"
    return "combine"
