Skip to content

F25: Hard budget enforcement — reject events when budget exceeded#3

Merged
peterkacerik merged 2 commits into
mainfrom
F25-hard-budget-enforcement
Mar 27, 2026
Merged

F25: Hard budget enforcement — reject events when budget exceeded#3
peterkacerik merged 2 commits into
mainfrom
F25-hard-budget-enforcement

Conversation

@peterkacerik
Copy link
Copy Markdown
Contributor

Summary

  • Add enforceLimit boolean to WorkspaceBudget model (default false)
  • When enforceLimit=true and monthly spend >= budget, POST /api/ingest and POST /api/ingest/otlp return 429 with X-Budget-Exceeded: true header
  • In-memory caches (30s TTL) for budget config and monthly spend to keep latency <5ms
  • BudgetPanel UI: "Enforce limit" toggle with confirmation dialog
  • Dashboard banners: yellow at 90%, red at 100% with "Increase budget" link
  • Budget API accepts enforceLimit in POST body

Task

Task file: aispendguard-tasks/active/features/F25-hard-budget-enforcement.md

Changes

File Change
prisma/schema.prisma Add enforceLimit Boolean @default(false) to WorkspaceBudget
lib/billing/budget.ts Add caches, checkBudgetEnforcement(), update BudgetConfig type
app/api/ingest/route.ts Budget enforcement gate after event-limit check
app/api/ingest/otlp/route.ts Same enforcement gate for OTLP endpoint
app/api/budgets/route.ts Accept enforceLimit boolean in POST
app/(app)/billing/budget-panel.tsx Enforce limit toggle, display, confirmation dialog
app/(app)/dashboard/page.tsx Budget enforcement banners at 90%/100%
app/components/budget-enforcement-banner.tsx New dismissible banner component

Testing

  • npx tsc --noEmit passes
  • Existing tests pass
  • Budget enforcement returns 429 with X-Budget-Exceeded header
  • Toggle visible only when budget exists
  • Confirmation dialog on enable

Documentation

SDK docs and architecture docs to be updated as part of docs sync.

- Add BudgetExceededInfo type and onBudgetExceeded callback to ClientConfig
- Add budgetBackoffMs config option (default 5 min)
- Circuit breaker: on 429 + X-Budget-Exceeded, silently drop events
  for backoff period, then probe with one request
- BudgetExceededError skips retry loop (Sentinel amendment #1)
- Single warning log + callback when entering budget-exceeded mode
- Fire-and-forget contract preserved

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@peterkacerik
Copy link
Copy Markdown
Contributor Author

Code Review — Sentinel

Verdict: REQUEST CHANGES — missing test file

Blocking Issue

Missing circuit breaker tests (High)
The implementation plan specified tests/circuit-breaker.test.mjs covering:

  • SDK enters circuit breaker on 429 + X-Budget-Exceeded
  • Events silently dropped during backoff
  • Probe retry after backoff expires
  • onBudgetExceeded callback fires once (not per-event)

No test file was included. The circuit breaker is a state machine with 3 states — this must be tested.

Code Quality — PASSED ✓

  • BudgetExceededError correctly skips retries in sendWithRetry() (amendment feat: Anthropic exact cost fields for SDK #1) — well implemented
  • Circuit breaker state machine in trackUsage() — correct
  • send() detects 429 + X-Budget-Exceeded header — correct
  • Re-throws BudgetExceededError in catch block — correct
  • onBudgetExceeded fires once per circuit break cycle — correct
  • Fire-and-forget contract preserved
  • BudgetExceededInfo exported correctly
  • budgetBackoffMs defaults to 300,000ms — correct

Security — PASSED ✓

No concerns. Client-side library.

Action Required

  1. Add tests/circuit-breaker.test.mjs with specified scenarios
  2. Re-request review from Sentinel

4 test scenarios:
- Enters circuit breaker on 429 + X-Budget-Exceeded, drops subsequent events
- Fires onBudgetExceeded callback exactly once
- Retries probe after backoff expires
- Does not enter circuit breaker on regular 429 (no header)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@peterkacerik
Copy link
Copy Markdown
Contributor Author

Sentinel Re-review: APPROVED ✅

SDK changes verified: BudgetExceededError skips retries, circuit breaker state machine correct, onBudgetExceeded fires once, 4 tests pass. Ready to merge.

@peterkacerik
Copy link
Copy Markdown
Contributor Author

Sentinel — Merge Approved

B18 resolved. App PR #72 deployed successfully. SDK PR #3 unblocked.

Code review was approved in previous session. Circuit breaker, BudgetExceededError, onBudgetExceeded callback all verified.

@peterkacerik peterkacerik merged commit 8ba01eb into main Mar 27, 2026
3 checks passed
@peterkacerik peterkacerik deleted the F25-hard-budget-enforcement branch March 27, 2026 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant