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
- Application Manager is running with the cost-governance subsystem enabled (Phase 3).
- The tenant exists and has a Top-tier
BudgetCap (enforced at tenant creation — see UC-016).
- The acting administrator is authenticated and holds the relevant cost capability for the action.
- For enforcement during a call: an active
AgentPlan and a ProviderAccount / ModelDeployment chain are resolvable for the agent.
- The model catalogue resolves a computed cost for the target deployment (unknown model → fail-closed; out of scope here).
Postconditions
Success Postconditions
- The cap configuration change is persisted and recorded in
AuditLog with before/after values and the acting principal.
- For a permitted call: every applicable cap has confirmed headroom and a
LlmSpendReservation is held (see UC-034).
- For an
ApprovalRequired exhaustion: a BudgetCapApproval record captures the approving admin, cap, amount, and approved window; the reservation proceeds.
- The effective cap tree returned by
GetBudgetTree reflects the change.
Failure Postconditions
- A
HardStop cap with no headroom denies the call with BudgetExhausted; no reservation is created.
- An invalid cap (e.g., a child tier exceeding its parent) is rejected; no row is persisted.
- Tenant creation without a Top-tier cap is rejected by the
CreateTenantCommand validator.
- 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
- Administrator selects the tier (
Top, Tenant, Subscription, Provider, Model) in the visual hierarchy editor.
- System scopes the cap to the relevant nullable identifier (
TenantId, SubscriptionId, ProviderId, ModelDeploymentId, or AgentId) based on tier.
- Administrator sets
Period (Day, Week, Month), LimitAmount, Currency, EnforcementMode (default HardStop), the MidStreamMode sub-flag (default Strict within HardStop), and alert thresholds.
- System validates the no-escalation rule: the cap's
LimitAmount must not exceed the effective limit of the tier above it.
CreateBudgetCap / UpdateBudgetCap command persists the row through IBudgetCapRepository.
- System writes a
BudgetCapAuditEntry (folded into AuditLog) with before/after values.
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
CapLatticeResolver collects every applicable cap across enabled tiers: Agency-Top → Tenant → Provider → ModelDeployment → per-agent overlay.
- For each cap, the resolver sums period-to-date spend (from
SpendRollup + live held reservations) for that cap's scope and period.
- The resolver confirms
periodSpend + requestedAmount ≤ LimitAmount for every cap.
- If all
HardStop caps have headroom, the resolver returns success; SoftAlert threshold breaches are flagged but not gating.
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
CapLatticeResolver finds a HardStop cap where periodSpend + requestedAmount > LimitAmount.
- The resolver returns a denial naming the exhausted cap and tier.
PlanBudgetManager does not create a reservation; it returns BudgetExhausted.
- The gateway surfaces
BudgetExhausted to the agent; no automatic retry is performed on the AM side.
- 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
CapLatticeResolver detects exhaustion on an ApprovalRequired cap.
- The reservation is set to
Status = PendingApproval rather than denied; the in-flight call is parked.
- The cap-tree editor's PendingApproval queue surfaces the reservation (cap, agent, amount, plan).
- An admin holding
cost.cap.approve clicks approve; POST /api/budgets/approvals/{reservationId}/approve fires ApproveCapExhaustion.
- System writes a
BudgetCapApproval (admin, cap, amount, approved window) and transitions the reservation to Held.
- 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
- During headroom resolution,
CapLatticeResolver detects that period spend crossed a SoftAlert threshold.
- The call is not gated; the reservation proceeds normally.
- System emits an alert via
IEmailService (and/or the dashboard panel) to the tenant's configured recipients.
- 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
CreateTenantCommand validator requires a Top-tier BudgetCap to be supplied.
- If the tenant
Status = Active, the default Top cap is $0/month (fail-closed — admin must raise it deliberately).
- If the tenant
Status = Trial, the default Top cap is $25/month.
- On agent registration, a per-agent overlay cap is created at
Tenant.Top / N (N configurable, default 10) unless overridden.
- 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 |