# Budget Controller

## Purpose

The BudgetController is the central enforcement point for all execution limits. Before **every** model call, the orchestrator must call `budget.pre_call_check()`. The response depends on `throttle_behavior`: abort mode denies the call and triggers a hard stop; degradation modes allow the call but switch to a local adapter.

## Tracked Metrics

| Metric | Description |
|---|---|
| `total_calls_used` | Number of model calls made so far |
| `premium_calls_used` | Number of premium model calls (determined by `AdapterConfig.is_premium`) |
| `start_time` | Wall-clock start of the run |
| `stage_attempts` | Per-stage attempt counters |
| `throttle_events` | List of throttle detections |

## Configurable Limits (Defaults)

| Limit | Default | Description |
|---|---|---|
| `max_total_calls` | 10 | Hard cap on total model invocations |
| `max_premium_calls` | 6 | Hard cap on premium model invocations |
| `max_wall_time_seconds` | 900 | 15-minute wall-clock limit |
| `max_critique_rounds` | 2 | Maximum PRD critique/revision cycles |

## Throttle Behaviour

When a limit is reached, `throttle_behavior` determines the action:

- `abort` — hard stop, record `stop_reason`.
- `degrade_to_local` — switch to local adapter for remaining stages.
- `retry_then_degrade` — allow one more call at original tier, then degrade to local on subsequent breaches.

## Critique Round Gating

`max_critique_rounds` controls how many critique/revision cycles are executed. Setting it to 0 skips PRD_CRITIQUE_R1 and PRD_REVISION_R1 entirely (and consequently R2). The `can_critique()` method gates each critique stage.

## Invariants

1. No model call may proceed without a passing budget check.
2. Budget state is monotonically increasing (calls cannot be "refunded").
3. Wall-time is checked at call boundaries, not mid-execution.
4. On hard stop, the manifest is still written with `stop_reason` populated.
