feat(budgets): weekly budgets, a refusal that names the limit, a ceiling on what may be saved #100

Merged
james.maes merged 1 commits from feat/weekly-budget into develop 2026-11-11 16:08:13 +00:00
Owner

Why

A host wants one spending limit for everyone, 500 US dollars a week, that an administrator can change in the application. Assistant > Budgets already is that setting (scope, period, dollars, warning percent, action at the limit, overrides, spend shown on the Assistant home), but a budget could only count over a UTC day or a UTC calendar month.

What changes

Area Change
Period WEEK: the ISO week in UTC, Monday 00:00:00 UTC up to the next Monday 00:00:00 UTC. DAY and MONTH are unchanged. No schema change: period is text.
Refusal at the limit Says which limit, how much, when it starts again and who can change it: "The assistant has reached the weekly limit of $500.00 of the budget 'Everyone, a week'. It starts again on Monday 19 October 2026 at 00:00 UTC. An administrator can raise the limit or grant an override." A new question gets it with HTTP 429 and code BUDGET; a running answer ends with it before its next model call.
Warning Says "weekly" for a weekly budget (it said "monthly" for anything that was not a day).
Ceiling New config budgetCostLimitCeilingUsd (default 1,000,000; null for none): a cost budget above it is refused when saved. Token budgets are not affected.
Who may save A budget is created or changed only by a session that holds assistant.admin. The table's permission rules already refused anybody else through the UI API; the save rule now also refuses, as it already did for overrides.
When a change applies Budgets are read from their table at every question and before every model call and are not cached, so a changed limit applies at once. Already so; now stated and tested.

Unit, for the record: cost is counted in micro-USD, millionths of a US dollar (ModelPricing.costMicros: a price of P dollars per million tokens is P micros per token). A COST_USD budget's limit is in dollars; BudgetService.snapshot divides the summed micros by a million.

Tests

BudgetServiceTest, five new tests:

  • weekRunsFromMondayToMondayInUtc: Sunday 23:59:59 UTC belongs to the week before, Monday 00:00:00 UTC starts the week; a Sunday evening in Chicago that is already Monday in UTC counts in the new week; a week across a year end.
  • weeklyLimitRefusesAtFiveHundredDollarsAndStartsAgainOnMonday: 499,999,999 micros are 499.999999 dollars and only warn; one micro more refuses with the sentence; the next Monday nothing is refused.
  • aQuestionAtTheWeeklyLimitIsRefusedWithTheReason: HTTP 429, code BUDGET, the sentence.
  • aChangedLimitAppliesToTheNextEvaluation: raise, lower and turn off, on the same running service.
  • onlyAnAdministratorSavesABudgetAndOnlyASaneOne: unknown period, zero and negative limits, the ceiling (default and configured), and a person without assistant.admin refused by the table's permission and by the save.

./mvnw -B verify on this branch: BUILD SUCCESS, 413 tests in qbit-assistant and every other module green, no skip flags.

🤖 Generated with Claude Code

## Why A host wants one spending limit for everyone, 500 US dollars a week, that an administrator can change in the application. **Assistant > Budgets** already is that setting (scope, period, dollars, warning percent, action at the limit, overrides, spend shown on the Assistant home), but a budget could only count over a UTC day or a UTC calendar month. ## What changes | Area | Change | |---|---| | Period | `WEEK`: the ISO week in UTC, Monday 00:00:00 UTC up to the next Monday 00:00:00 UTC. `DAY` and `MONTH` are unchanged. No schema change: `period` is text. | | Refusal at the limit | Says which limit, how much, when it starts again and who can change it: "The assistant has reached the weekly limit of $500.00 of the budget 'Everyone, a week'. It starts again on Monday 19 October 2026 at 00:00 UTC. An administrator can raise the limit or grant an override." A new question gets it with HTTP 429 and code `BUDGET`; a running answer ends with it before its next model call. | | Warning | Says "weekly" for a weekly budget (it said "monthly" for anything that was not a day). | | Ceiling | New config `budgetCostLimitCeilingUsd` (default 1,000,000; null for none): a cost budget above it is refused when saved. Token budgets are not affected. | | Who may save | A budget is created or changed only by a session that holds `assistant.admin`. The table's permission rules already refused anybody else through the UI API; the save rule now also refuses, as it already did for overrides. | | When a change applies | Budgets are read from their table at every question and before every model call and are not cached, so a changed limit applies at once. Already so; now stated and tested. | Unit, for the record: cost is counted in micro-USD, millionths of a US dollar (`ModelPricing.costMicros`: a price of P dollars per million tokens is P micros per token). A `COST_USD` budget's limit is in dollars; `BudgetService.snapshot` divides the summed micros by a million. ## Tests `BudgetServiceTest`, five new tests: - `weekRunsFromMondayToMondayInUtc`: Sunday 23:59:59 UTC belongs to the week before, Monday 00:00:00 UTC starts the week; a Sunday evening in Chicago that is already Monday in UTC counts in the new week; a week across a year end. - `weeklyLimitRefusesAtFiveHundredDollarsAndStartsAgainOnMonday`: 499,999,999 micros are 499.999999 dollars and only warn; one micro more refuses with the sentence; the next Monday nothing is refused. - `aQuestionAtTheWeeklyLimitIsRefusedWithTheReason`: HTTP 429, code `BUDGET`, the sentence. - `aChangedLimitAppliesToTheNextEvaluation`: raise, lower and turn off, on the same running service. - `onlyAnAdministratorSavesABudgetAndOnlyASaneOne`: unknown period, zero and negative limits, the ceiling (default and configured), and a person without `assistant.admin` refused by the table's permission and by the save. `./mvnw -B verify` on this branch: BUILD SUCCESS, 413 tests in `qbit-assistant` and every other module green, no skip flags. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
james.maes added 1 commit 2026-11-11 16:00:18 +00:00
feat(budgets): weekly budgets, a refusal that names the limit, a ceiling on what may be saved
maven / build (push) Successful in 4m52s
build / maven (push) Successful in 4m52s
48cca86285
A budget (Assistant > Budgets) can count over a WEEK: the ISO week in UTC,
Monday 00:00:00 UTC up to the next Monday 00:00:00 UTC. DAY and MONTH are
unchanged and there is no schema change (period is text).

The refusal at a limit now says which limit, how much, when the count starts
again and who can change it: "The assistant has reached the weekly limit of
$500.00 of the budget 'Everyone, a week'. It starts again on Monday 19 October
2026 at 00:00 UTC. An administrator can raise the limit or grant an override."
The warning says "weekly" for a weekly budget.

New config budgetCostLimitCeilingUsd (default 1,000,000, null for none): a
cost budget above it is refused when saved. A budget is created or changed
only by a session that holds assistant.admin; the save rule now checks it as
it already did for overrides, besides the table's permission rules.

Budgets are read from their table at every evaluation and are not cached, so
a changed limit applies to the next question. That was already so; it is now
stated and tested.

Tests (BudgetServiceTest): the week boundary in UTC, including a Sunday
evening in Chicago that is Monday in UTC and a week across a year end; the
micro-USD to dollar conversion at the limit (499,999,999 micros warn, one more
refuses); the refusal sentence and HTTP 429 BUDGET for a new question; a
changed limit taking effect on the running service; the ceiling; and a person
without assistant.admin refused by the table's permission and by the save.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
james.maes merged commit f5bf3e1d04 into develop 2026-11-11 16:08:13 +00:00
Sign in to join this conversation.