Use Case 032: Budget Cap Lattice & Enforcement

Overview

Property Value
Use Case ID UC-032
Use Case Name Budget Cap Lattice & Enforcement
Module Agent Identity — Cost Governance
Priority Critical
Status ✅ Implemented
Version 1.0
Last Updated June 15, 2026

Implementation status (agent-identity release, June 2026). Implemented: BudgetCap + CapLatticeResolver walk the Top→Tenant→Subscription→Provider→Model lattice; enforcement modes SoftAlert / ApprovalRequired / HardStop with MidStreamMode.Strict, and fail-closed cold-start defaults (HardStop + Strict). Admin UI at /budget-caps (incl. an approvals queue), API at /api/budgets.

Description

This use case describes the budget cap lattice — the hierarchical set of spending limits that bound LLM cost across the agency, each tenant, each provider account, each model deployment, and each individual agent. It is the single enforcement point for all agent-incurred LLM spend in the Application Manager agent-identity release. A generic BudgetCap entity expresses a cap at any tier of the lattice, and the CapLatticeResolver service walks every applicable cap from the agency top down to the per-agent overlay, confirming headroom before any spend is reserved.

Caps carry one of three enforcement modes — SoftAlert (notify without gating), ApprovalRequired (a critical workload may proceed past a cap with one-click admin approval), and HardStop (deny with BudgetExhausted; the default safety primitive). The lattice is fail-closed at cold start: a Top-tier cap is mandatory at tenant creation. This use case is part of the agent-identity release; the SDK master design lives in riptide-sdk/docs/plans/AGENT-IDENTITY-ARCHITECTURE.md and the Application Manager plan in docs/internal/agent-identity-plan.md (Section 3, §10 cost-governance decisions).

Actors

Actor Description Role
Cost Administrator Admin holding cost.cap.edit / cost.cap.approve capabilities Primary
Agent (via LLM Gateway) Non-human identity whose spend is being governed Primary
CapLatticeResolver Service that walks the lattice and confirms headroom Supporting
LLM Gateway (LlmDispatchService) Calls the resolver during the request lifecycle Supporting
System Application Manager platform Supporting
SDK Consumer / RAF Component External caller minting plans and dispatching LLM calls External

Preconditions

  1. Application Manager is running with the cost-governance subsystem enabled (Phase 3).
  2. The tenant exists and has a Top-tier BudgetCap (enforced at tenant creation — see UC-016).
  3. The acting administrator is authenticated and holds the relevant cost capability for the action.
  4. For enforcement during a call: an active AgentPlan and a ProviderAccount / ModelDeployment chain are resolvable for the agent.
  5. The model catalogue resolves a computed cost for the target deployment (unknown model → fail-closed; out of scope here).

Postconditions

Success Postconditions

  1. The cap configuration change is persisted and recorded in AuditLog with before/after values and the acting principal.
  2. For a permitted call: every applicable cap has confirmed headroom and a LlmSpendReservation is held (see UC-034).
  3. For an ApprovalRequired exhaustion: a BudgetCapApproval record captures the approving admin, cap, amount, and approved window; the reservation proceeds.
  4. The effective cap tree returned by GetBudgetTree reflects the change.

Failure Postconditions

  1. A HardStop cap with no headroom denies the call with BudgetExhausted; no reservation is created.
  2. An invalid cap (e.g., a child tier exceeding its parent) is rejected; no row is persisted.
  3. Tenant creation without a Top-tier cap is rejected by the CreateTenantCommand validator.
  4. An approval attempt without cost.cap.approve is rejected; the reservation remains PendingApproval until it expires.

Primary Flow Sequence

sequenceDiagram
    participant GW as LLM Gateway
    participant PBM as PlanBudgetManager
    participant CLR as CapLatticeResolver
    participant DB as IdentityDbContext
    participant Audit as AuditLog

    GW->>PBM: ReserveSpend(planId, estimatedMax)
    PBM->>PBM: check reservation <= plan.remainingBudget
    PBM->>CLR: ConfirmHeadroom(tenant, provider, deployment, agent, amount)
    CLR->>DB: load caps for every enabled tier
    DB-->>CLR: Top, Tenant, Provider, Model, per-agent overlay
    loop each applicable cap
        CLR->>CLR: period spend + amount <= LimitAmount ?
    end
    CLR-->>PBM: headroom OK (all HardStop caps satisfied)
    PBM->>DB: insert LlmSpendReservation (Held)
    PBM->>Audit: record reservation (actor chain)
    PBM-->>GW: reservationId, Held

UC-032a: Create / Edit a Budget Cap

Triggers

  • Administrator opens the cap-tree editor in BudgetsController and adds or edits a cap.
  • An API client calls POST /api/budgets/caps or PUT /api/budgets/caps/{id}.

Basic Flow

  1. Administrator selects the tier (Top, Tenant, Subscription, Provider, Model) in the visual hierarchy editor.
  2. System scopes the cap to the relevant nullable identifier (TenantId, SubscriptionId, ProviderId, ModelDeploymentId, or AgentId) based on tier.
  3. Administrator sets Period (Day, Week, Month), LimitAmount, Currency, EnforcementMode (default HardStop), the MidStreamMode sub-flag (default Strict within HardStop), and alert thresholds.
  4. System validates the no-escalation rule: the cap's LimitAmount must not exceed the effective limit of the tier above it.
  5. CreateBudgetCap / UpdateBudgetCap command persists the row through IBudgetCapRepository.
  6. System writes a BudgetCapAuditEntry (folded into AuditLog) with before/after values.
  7. GetBudgetTree recomputes the effective cap tree.

Alternative Flows

  • A1: Child exceeds parent — Validation rejects the cap; editor redisplays with the no-escalation error.
  • A2: Duplicate cap at same scope+period — Rejected; existing cap must be edited instead.
  • A3: Delete cap — DeleteBudgetCap removes a non-Top cap; deleting a Top cap is rejected (Top is mandatory).
  • A4: Currency mismatch with parent — Rejected; all caps in a lattice branch share the tenant currency.

UC-032b: Resolve the Effective Cap Tree (Headroom Check)

Triggers

  • LlmDispatchService requests a reservation; PlanBudgetManager invokes CapLatticeResolver.
  • An admin calls GET /api/budgets/tree for introspection.

Basic Flow

  1. CapLatticeResolver collects every applicable cap across enabled tiers: Agency-Top → Tenant → Provider → ModelDeployment → per-agent overlay.
  2. For each cap, the resolver sums period-to-date spend (from SpendRollup + live held reservations) for that cap's scope and period.
  3. The resolver confirms periodSpend + requestedAmount ≤ LimitAmount for every cap.
  4. If all HardStop caps have headroom, the resolver returns success; SoftAlert threshold breaches are flagged but not gating.
  5. GetBudgetTree returns the cap tree with per-node limit, period-to-date spend, and remaining headroom for the UI.

Alternative Flows

  • A1: A tier is disabled — That tier contributes no cap; the resolver skips it (only Top is mandatory).
  • A2: Per-agent overlay absent — Default overlay = Tenant.Top / N (N configurable, default 10) is applied at agent creation; the resolver uses it.
  • A3: Multiple caps at the same scope — The most restrictive (lowest remaining headroom) governs.

UC-032c: HardStop Denial

Triggers

  • A reservation request hits a HardStop cap with insufficient headroom.

Basic Flow

  1. CapLatticeResolver finds a HardStop cap where periodSpend + requestedAmount > LimitAmount.
  2. The resolver returns a denial naming the exhausted cap and tier.
  3. PlanBudgetManager does not create a reservation; it returns BudgetExhausted.
  4. The gateway surfaces BudgetExhausted to the agent; no automatic retry is performed on the AM side.
  5. The denial is recorded in AuditLog with the actor chain and the cap that triggered it.

Alternative Flows

  • A1: Mid-stream exhaustion (Strict) — A streaming response whose governing cap is HardStop + MidStreamMode = Strict aborts mid-stream, returning BudgetExhausted with partial content; no auto-continuation.
  • A2: Mid-stream exhaustion (Lenient) — The in-flight stream is allowed to complete against its existing reservation; the next reservation is denied.

UC-032d: ApprovalRequired Approval Flow

Triggers

  • A reservation hits a cap whose EnforcementMode = ApprovalRequired.

Basic Flow

  1. CapLatticeResolver detects exhaustion on an ApprovalRequired cap.
  2. The reservation is set to Status = PendingApproval rather than denied; the in-flight call is parked.
  3. The cap-tree editor's PendingApproval queue surfaces the reservation (cap, agent, amount, plan).
  4. An admin holding cost.cap.approve clicks approve; POST /api/budgets/approvals/{reservationId}/approve fires ApproveCapExhaustion.
  5. System writes a BudgetCapApproval (admin, cap, amount, approved window) and transitions the reservation to Held.
  6. The gateway proceeds with the call; the spend reconciles normally.

Alternative Flows

  • A1: No approval before expiry — The reservation expires (Status = Expired); the call fails with BudgetExhausted.
  • A2: Approver lacks capability — 403; reservation stays PendingApproval.
  • A3: Approval window scoped — The BudgetCapApproval window bounds how long subsequent reservations may bypass that cap; beyond the window, approval is required again.
sequenceDiagram
    participant GW as LLM Gateway
    participant CLR as CapLatticeResolver
    participant DB as IdentityDbContext
    participant Queue as PendingApproval Queue
    participant Admin as Cost Administrator

    GW->>CLR: reserve(amount) on ApprovalRequired cap
    CLR->>CLR: periodSpend + amount > LimitAmount
    CLR->>DB: reservation Status = PendingApproval
    CLR-->>GW: parked (PendingApproval)
    Queue->>Admin: show pending reservation (cap, agent, amount)
    Admin->>DB: POST approvals/{rid}/approve (ApproveCapExhaustion)
    DB->>DB: write BudgetCapApproval (admin, cap, window)
    DB->>DB: reservation Status = Held
    DB-->>GW: proceed
    GW->>GW: dispatch + reconcile

UC-032e: SoftAlert Threshold Notification

Triggers

  • Period-to-date spend on a SoftAlert cap crosses a configured alert threshold (e.g., 80%).

Basic Flow

  1. During headroom resolution, CapLatticeResolver detects that period spend crossed a SoftAlert threshold.
  2. The call is not gated; the reservation proceeds normally.
  3. System emits an alert via IEmailService (and/or the dashboard panel) to the tenant's configured recipients.
  4. The threshold crossing is recorded so the alert is not re-fired for the same threshold within the same period.

Alternative Flows

  • A1: Multiple thresholds (80%, 100%) — Each threshold fires once per period as it is crossed.
  • A2: Threshold breach after period rollover — Counters reset at the boundary; thresholds re-arm for the new period.

UC-032f: Cold-Start Tenant Cap Requirement

Triggers

  • A new tenant is created (see UC-016) or an agent is registered with no viable headroom.

Basic Flow

  1. CreateTenantCommand validator requires a Top-tier BudgetCap to be supplied.
  2. If the tenant Status = Active, the default Top cap is $0/month (fail-closed — admin must raise it deliberately).
  3. If the tenant Status = Trial, the default Top cap is $25/month.
  4. On agent registration, a per-agent overlay cap is created at Tenant.Top / N (N configurable, default 10) unless overridden.
  5. If creating the agent would leave no viable headroom under the Top cap, agent creation is blocked.

Alternative Flows

  • A1: Missing Top cap at tenant creation — CreateTenantCommand is rejected; tenant is not created.
  • A2: Active tenant left at $0 — All agent calls HardStop until an admin raises the Top cap.
  • A3: Overlay larger than remaining Top headroom — Overlay is clamped to remaining Top headroom (no-escalation).

API Endpoints

Method Path Auth Purpose
POST /api/budgets/caps X-Api-Key + cost.cap.edit Create a budget cap at a tier
PUT /api/budgets/caps/{id} X-Api-Key + cost.cap.edit Update an existing cap
DELETE /api/budgets/caps/{id} X-Api-Key + cost.cap.edit Delete a non-Top cap
GET /api/budgets/tree X-Api-Key + cost.cap.view Effective cap tree for the calling tenant/agent
POST /api/budgets/approvals/{reservationId}/approve X-Api-Key + cost.cap.approve Approve an ApprovalRequired exhaustion event
// POST /api/budgets/caps
{
  "tier": "Provider",
  "tenantId": "5f1c0e2a-2c54-7b91-a1d0-9b2e44ab12cd",
  "providerId": "anthropic-prod-account",
  "period": "Month",
  "limitAmount": 2500.00,
  "currency": "USD",
  "enforcementMode": "HardStop",
  "midStreamMode": "Strict",
  "alertThresholds": [0.8, 1.0]
}

// 201 Created
{
  "capId": "b73d2f10-9a4c-7e2f-bb55-0c3a1de98a77",
  "tier": "Provider",
  "scope": { "tenantId": "5f1c0e2a-2c54-7b91-a1d0-9b2e44ab12cd", "providerId": "anthropic-prod-account" },
  "period": "Month",
  "limitAmount": 2500.00,
  "currency": "USD",
  "enforcementMode": "HardStop",
  "midStreamMode": "Strict",
  "periodToDateSpend": 0.00,
  "remainingHeadroom": 2500.00,
  "createdAt": "2026-06-09T14:05:33Z"
}

Business Rules

Rule Description
BR-1 A Top-tier cap is mandatory for every tenant; enforced in CreateTenantCommand validator.
BR-2 No-escalation: no cap may exceed the effective limit of the tier above it; every tier is bounded by the cap above.
BR-3 HardStop is the default EnforcementMode; deny on exhaustion returns BudgetExhausted.
BR-4 MidStreamMode defaults to Strict within HardStop; Strict aborts mid-stream, Lenient lets the in-flight stream complete.
BR-5 ApprovalRequired parks the reservation as PendingApproval; an admin with cost.cap.approve may approve to proceed.
BR-6 SoftAlert never gates; it notifies on threshold crossing and proceeds.
BR-7 Active tenants default Top = $0/month; Trial tenants default Top = $25/month.
BR-8 Per-agent overlay default = Tenant.Top / N (N configurable, default 10), clamped to remaining Top headroom.
BR-9 The Top cap cannot be deleted; lower-tier caps may be deleted.
BR-10 CapLatticeResolver is the single enforcement point; all reservations pass through it.
BR-11 Every cap edit and every approval is written to AuditLog with before/after values and acting principal.
BR-12 Each alert threshold fires at most once per cap per period; thresholds re-arm at period rollover.

Data Requirements

BudgetCap

Field Type Constraints
Id Guid (UUIDv7) Primary key
Tier enum Top, Tenant, Subscription, Provider, Model
TenantId Guid? Nullable; set by tier scope
SubscriptionId Guid? Nullable; set for Subscription tier
ProviderId string? Nullable; set for Provider tier
ModelDeploymentId Guid? Nullable; set for Model tier
AgentId Guid? Nullable; set for per-agent overlay
Period enum Day, Week, Month
LimitAmount decimal ≥ 0; ≤ parent tier limit (no-escalation)
Currency string ISO 4217; matches branch currency
EnforcementMode enum SoftAlert, ApprovalRequired, HardStop (default)
MidStreamMode enum Strict (default), Lenient; relevant under HardStop
AlertThresholds decimal[] Fractions of LimitAmount (e.g., 0.8, 1.0)
CreatedAt DateTimeOffset Set on create
CreatedBy string Acting principal id

BudgetCapApproval

Field Type Constraints
Id Guid (UUIDv7) Primary key
BudgetCapId Guid FK → BudgetCap
ReservationId Guid FK → LlmSpendReservation
ApprovedAmount decimal Amount authorized past the cap
ApprovedBy string Admin principal id (cost.cap.approve)
ApprovedWindowStart DateTimeOffset Start of authorized window
ApprovedWindowEnd DateTimeOffset End of authorized window
ApprovedAt DateTimeOffset Set on approval

Security Considerations

  • Authentication: All API routes require X-Api-Key; admin UI uses cookie auth. Approval routes additionally require the cost.cap.approve capability.
  • Authorization / capabilities: cost.cap.view, cost.cap.edit, cost.cap.approve (capability-based RBAC; admins compose them into roles).
  • Data protection: Caps carry no secrets; cap amounts are tenant-scoped and only visible within the tenant (or to agency-scoped viewers).
  • Audit (actor chain): Every cap create/edit/delete and every exhaustion approval is recorded in AuditLog with before/after values, the acting principal, and — for delegated agent calls — the full actor chain (agent ⇽ on-behalf-of subject).
  • Fail-closed: No tenant exists without a Top cap; unknown models and missing headroom deny rather than allow.

Testing Scenarios

ID Scenario Expected Result
T-1 Create a valid Provider-tier cap below its Tenant cap 201; cap persisted; audit row written
T-2 Create a cap exceeding its parent tier limit Rejected with no-escalation error; no row persisted
T-3 Create duplicate cap at same scope+period Rejected
T-4 Delete a non-Top cap 200; cap removed; audit recorded
T-5 Attempt to delete the Top cap Rejected; Top is mandatory
T-6 GET /api/budgets/tree after edits Returns updated tree with limits, period-to-date spend, headroom
T-7 Reservation with full headroom across all tiers Reservation Held; call proceeds
T-8 Reservation hits a HardStop cap with no headroom Denied with BudgetExhausted; no reservation created
T-9 Reservation hits an ApprovalRequired cap Reservation PendingApproval; appears in queue
T-10 Admin with cost.cap.approve approves a pending reservation BudgetCapApproval written; reservation Held; call proceeds
T-11 Approver lacking cost.cap.approve attempts approval 403; reservation stays PendingApproval
T-12 PendingApproval reservation not approved before expiry Reservation Expired; call fails with BudgetExhausted
T-13 SoftAlert cap crosses 80% threshold Alert sent; call proceeds; threshold not re-fired same period
T-14 SoftAlert crosses 100% Second alert fires; still not gating
T-15 Streaming call under HardStop + Strict exhausts mid-stream Stream aborts with BudgetExhausted + partial content; no continuation
T-16 Streaming call under HardStop + Lenient exhausts mid-stream In-flight stream completes; next reservation denied
T-17 Create Active tenant without a Top cap CreateTenantCommand rejected
T-18 Create Active tenant with default Top Top = $0/month; all agent calls HardStop until raised
T-19 Create Trial tenant Top defaults to $25/month
T-20 Register agent — per-agent overlay created Overlay = Tenant.Top / N (default N=10)
T-21 Register agent when no Top headroom remains Agent creation blocked
T-22 Period rollover crossed Period-to-date counters reset; alert thresholds re-arm
T-23 Two caps at same scope, differing limits Most restrictive (lowest remaining headroom) governs
T-24 Disabled Provider tier Resolver skips it; remaining tiers still enforce
T-25 Approval window expiry Post-window reservations on the same cap require fresh approval
  • UC-016: Tenant Provisioning & Management — Top-tier cap requirement enforced at tenant creation.
  • UC-033: Agent Plan Lifecycle & Budget Delegation — plans reserve against the lattice via PlanBudgetManager.
  • UC-034: Spend Reservation & Reconciliation — reservations and the ledger that records governed spend.
  • UC-038: Cost Insights Dashboard — caps-status burn-rate projections and alert surfacing.

Revision History

Version Date Author Notes
1.0 June 9, 2026 Platform Architecture Team Initial draft