# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-working-memory-starvation-when-retrieval-and-scratchpad-allocations-mask-the-deficit.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-working-memory-starvation-when-retrieval-and-scratchpad-allocations-mask-the-deficit.warrant.json
# Markdown SHA-256:   7f1f6c15eb497251fc1f1fdbf837e43478b485a0f05540d4b49839d9787c7fe1
# Code AST SHA-256:   09a21e496fe365b2ff38795fa280d0bc522e007e9ba0bb4c2e97b06172f05f87
# Verified Classes:   WorkingMemoryStarvationError, BudgetState
# Defensive Guards:   1 fail-closed invariants (zero external deps)

from dataclasses import dataclass


class WorkingMemoryStarvationError(RuntimeError):
    """Raised when residual working memory is below the declared working-set
    requirement while total utilization remains under the eviction threshold.
    This is the silent starvation regime: no allocation failed, but reasoning
    cannot proceed with the state it must hold."""


@dataclass
class BudgetState:
    total_budget: int
    working_memory: int
    retrieval: int
    scratchpad: int
    eviction_threshold: float = 0.90

    @property
    def used(self) -> int:
        return self.working_memory + self.retrieval + self.scratchpad

    @property
    def utilization(self) -> float:
        return self.used / self.total_budget

    @property
    def residual_working_memory(self) -> int:
        # Working memory is allocated as residual: whatever the sticky
        # consumers have not claimed.
        return self.total_budget - self.retrieval - self.scratchpad


def arbitrate(state: BudgetState, working_set_requirement: int) -> BudgetState:
    """Gate a turn on residual-vs-requirement, not on the ceiling."""
    if state.utilization >= state.eviction_threshold:
        # Loud regime: normal eviction path handles this.
        return state

    if state.residual_working_memory < working_set_requirement:
        # Silent starvation: reclaim from sticky consumers in order of
        # recoverability before raising.
        if state.scratchpad > 0:
            state.scratchpad = 0  # recoverable by re-invocation
            if state.residual_working_memory >= working_set_requirement:
                return state
        if state.retrieval > 0:
            state.retrieval = 0  # recoverable by re-query
            if state.residual_working_memory >= working_set_requirement:
                return state

        raise WorkingMemoryStarvationError(
            f"residual working memory {state.residual_working_memory} < "
            f"required {working_set_requirement} at utilization "
            f"{state.utilization:.2f} (below eviction threshold "
            f"{state.eviction_threshold}); reclamation insufficient"
        )

    return state
