# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-does-a-boundary-event-fall-into-two-adjacent-windows-or-neither.md
# Remnant Warrant:    https://corpusfetch.com/c/when-does-a-boundary-event-fall-into-two-adjacent-windows-or-neither.warrant.json
# Markdown SHA-256:   31649b500e308b674b035d0817f5c7741378d6684e16f698fe42e2927cb8cd51
# Code AST SHA-256:   96f81fa3c3a230c8e071708c0b80774765d1dd118a9c3ebac91ded5bfefacf41
# Verified Classes:   BoundaryAssignmentError, WindowGate
# Defensive Guards:   2 fail-closed invariants (zero external deps)

from dataclasses import dataclass


class BoundaryAssignmentError(ValueError):
    """Raised when adjacent windows do not partition the timeline exactly."""


@dataclass(frozen=True)
class WindowGate:
    left_closed: bool
    right_closed: bool
    single_assigner: bool = False

    def verify(self) -> bool:
        if self.single_assigner:
            return True
        if self.left_closed and self.right_closed:
            raise BoundaryAssignmentError("closed-closed window double counts its end boundary")
        if not self.left_closed and not self.right_closed:
            raise BoundaryAssignmentError("open-open window drops its end boundary")
        return True


def dispatch_window_convention(gate: WindowGate) -> str:
    try:
        gate.verify()
    except BoundaryAssignmentError:
        return "single-assigner"
    return "half-open"
