What minimum viable reasoning survives when the reasoning budget collapses mid-task?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session is mid-task with an allocated reasoning budget (tokens, compute, or time), and the budget collapses unexpectedly (throttling, partial outage, or priority preemption), requiring the session to determine what minimum reasoning is achievable in the residual budget. Does not apply to sessions that complete within budget or that have unbounded budgets.

Core Distinction

The separation between **full-budget reasoning** (the reasoning possible under the original allocation) and **minimum viable reasoning** (the reasoning achievable in the residual budget that still produces useful output). Relying on full-budget creates the **Budget Collapse Halting**: the session halts when budget shrinks, forfeiting all value of the partial reasoning, without attempting the minimum viable subset.

What Was Observed

In budget-constrained agent architectures, failing to bound reasoning to residual budget produces three primary failure modes:

  1. **Budget Shock Halt:** The session halts when budget collapses because continuing appears impossible, and the partial work is wasted.
  2. **Over-Continuing:** The session continues trying to complete the full reasoning under insufficient budget, producing degraded or truncated outputs that are worse than a scoped minimum.
  3. **Quality Blind Spot:** The session completes a minimum-viable subset but doesn't declare the scope reduction, so downstream consumers treat the output as full-scope.

Recorded Elements

  1. **`original-budget-receipt`:** The exact budget originally allocated and the reasoning scope it supported, so the baseline is documented.
  2. **`residual-budget-signature`:** The exact residual budget after collapse, with the collapse epoch, so the new constraint is known.
  3. **`minimum-viable-scope-receipt`:** The minimum viable reasoning scope achievable in residual budget, computed from the reasoning graph's essential components.
  4. **`scope-degradation-declaration-signature`:** The exact scope reduction declared in the output, so downstream consumers know the output's limits.

Condition of Validity

* **Applies when:** The session has an allocated budget, the budget can collapse mid-task, and residual budget supports a subset of full reasoning. * **Does not apply when:** The budget is unbounded, cannot collapse, or the task cannot be usefully subset.

Decision Boundary

  1. **Has the budget collapsed?** If no, full-budget reasoning applies. If yes, proceed.
  2. **Is `minimum-viable-scope-receipt` computable?** If yes, minimum viable scope is defined. If no, halting is the only honest option.
  3. **Is `scope-degradation-declaration-signature` present?** If yes, the reduction is disclosed. If no, output is misleading.
  4. **Conversely, when can the session produce output without scope declaration?** Only when the residual budget is verified sufficient for the original scope (no reduction needed), or the output is strictly provisional and downstream decisions await budget restoration.

Operational Effect

  1. **Value Preservation:** Partial reasoning is salvaged as minimum viable.
  2. **Over-Continuing Prevention:** Scoping prevents degraded full-attempts.
  3. **Downstream Integrity:** Scope reduction is disclosed.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class BudgetVerdict(Enum):
    NO_COLLAPSE = auto()
    MINIMUM_VIABLE_PRODUCED = auto()
    SCOPE_REDUCTION_UNDECLARED = auto()
    INSUFFICIENT_FOR_VIABLE = auto()

@dataclass(frozen=True)
class BudgetState:
    state_id: str
    budget_collapsed: bool
    residual_budget_ratio: float
    minimum_viable_scope_defined: bool
    scope_declared: bool

@dataclass(frozen=True)
class BudgetPolicy:
    min_viable_threshold: float

@dataclass(frozen=True)
class BudgetManifest:
    verdict: BudgetVerdict
    directive: str
    manifest_digest: str

class ReasoningBudgetCollapseGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_state(
        cls, state: BudgetState, policy: BudgetPolicy
    ) -> BudgetManifest:
        
        # Gate 1: No collapse
        if not state.budget_collapsed:
            digest = cls._hash(json.dumps({
                "state": state.state_id,
                "collapsed": False
            }, sort_keys=True))
            return BudgetManifest(
                verdict=BudgetVerdict.NO_COLLAPSE,
                directive=f"PROCEED: State {state.state_id} budget intact.",
                manifest_digest=digest
            )

        # Gate 2: Insufficient for viable
        if state.residual_budget_ratio < policy.min_viable_threshold:
            return BudgetManifest(
                verdict=BudgetVerdict.INSUFFICIENT_FOR_VIABLE,
                directive=f"HALT: State {state.state_id} residual {state.residual_budget_ratio:.2f} below viable threshold.",
                manifest_digest=cls._hash(f"INSUFFICIENT:{state.state_id}")
            )

        # Gate 3: Scope reduction undeclared
        if not state.scope_declared:
            return BudgetManifest(
                verdict=BudgetVerdict.SCOPE_REDUCTION_UNDECLARED,
                directive=f"HALT: State {state.state_id} scope reduced without declaration.",
                manifest_digest=cls._hash(f"UNDECLARED:{state.state_id}")
            )

        # Gate 4: Minimum viable produced
        digest = cls._hash(json.dumps({
            "state": state.state_id,
            "residual": f"{state.residual_budget_ratio:.3f}",
            "scope": "REDUCED"
        }, sort_keys=True))
        
        return BudgetManifest(
            verdict=BudgetVerdict.MINIMUM_VIABLE_PRODUCED,
            directive=f"PROCEED: State {state.state_id} minimum viable produced.",
            manifest_digest=digest
        )