Skip to content

fix(addie): reset daily cost cap at UTC midnight instead of rolling 24h - #6211

Open
coolmacool wants to merge 1 commit into
adcontextprotocol:mainfrom
coolmacool:fix/6048-addie-calendar-day-reset
Open

fix(addie): reset daily cost cap at UTC midnight instead of rolling 24h#6211
coolmacool wants to merge 1 commit into
adcontextprotocol:mainfrom
coolmacool:fix/6048-addie-calendar-day-reset

Conversation

@coolmacool

Copy link
Copy Markdown

Summary

  • Addie's daily conversation cost cap used a rolling 24h window (recorded_at > NOW() - interval), but the user-facing message says "try again tomorrow" — implying a calendar reset that never happened. A user capped at 6pm was still blocked at 8am the next day (only 14 of the required 24h elapsed). Fixes Bug: Daily Addie conversation limit not resetting after 24 hours #6048.
  • Switched the reset boundary to UTC midnight, computed inside Postgres (date_trunc('day', NOW() AT TIME ZONE 'UTC')) so the cutoff and the recorded_at timestamps it's compared against share one clock — an app-clock cutoff compared against DB-clock timestamps would reintroduce the same class of boundary bug this issue was about.
  • retryAfterMs now counts down to the next UTC midnight instead of a per-charge "24h anniversary."
  • Root cause was not a regression from the feat(addie): per-user Anthropic cost cap (#2790) #2946feat(cost-cap): resolve real tier for authenticated callers (#2945 f/u) #2969 cost-cap PRs as the issue speculated — the rolling window is original behavior from feat(addie): per-user Anthropic cost cap (#2790) #2946, unchanged since.
  • Secondary risk mitigation: added a warn log when an unmapped-WorkOS-identity Slack user (slack:<id> scope key) hits the member_free tier cap, so it's diagnosable from logs that they may be getting a tighter cap ($5/day) than their real subscription tier warrants, instead of requiring a support escalation to notice.
  • Documented an accepted tradeoff of the calendar-day boundary: a user can spend up to ~2x budget in the seconds straddling UTC midnight. Judged acceptable given caps are small ($3–$25) and this is abuse-defense, not billing enforcement — now called out explicitly in the module's "Known trade-offs" docstring.

Test plan

  • npx tsc --project server/tsconfig.json --noEmit — clean
  • npx vitest run server/tests/unit/claude-cost-tracker.test.ts server/tests/unit/claude-cost-tier-resolution.test.ts server/tests/unit/claude-client-cost-gate.test.ts — 50/50 pass. Rewrote the cost-tracker test suite's rolling-window block into a calendar-day-semantics block (mid-window unblock at midnight, exact 23:59:59.999/00:00:00 boundary, retryAfterMs correctness, no cross-day bleed).
  • Live-verified against a real local Postgres (migrated via npm run db:migrate), exercising PostgresStore directly rather than the in-memory DI store the unit tests use: backdated-yesterday charge excluded, today's charge blocks with retryAfterMs accurate to the millisecond, exact-midnight inclusive boundary, and the new Slack-unmapped warn log fires as designed.
  • code-reviewer and security-reviewer agents run; findings addressed (app/DB clock-skew fix, midnight-burst tradeoff documented).
  • node scripts/check-changeset-protocol-scope.cjs origin/main — passed, no changeset needed (server-only Addie change).

Risks / follow-ups

  • The unmapped-Slack-identity tier gap (Slack users without a linked WorkOS account always resolve to member_free) is now observable via logs but not fixed — that's a separate, larger identity-resolution feature, out of scope here.

The daily conversation cost cap used a rolling 24h window, but the
user-facing "try again tomorrow" message implies a calendar reset that
never happened — a user capped at 6pm was still blocked at 8am the next
day. Switch to a UTC-midnight boundary computed inside Postgres so the
cutoff and recorded charges share one clock, avoiding app/DB clock skew
at the boundary. Also log when an unmapped-WorkOS-identity Slack user
hits the tighter member_free cap, so that known tier-misattribution risk
is diagnosable from logs.

Fixes adcontextprotocol#6048
@aao-ipr-bot

aao-ipr-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

IPR Policy Agreement Required

@coolmacool — thanks for the contribution. Before this PR can be merged, the AgenticAdvertising.Org IPR Policy requires your agreement.

To agree, post a new comment on this PR with the exact phrase:

I have read the IPR Policy

Your signature is recorded once and covers all contributions to AAO repositories. See signatures/README.md for what gets recorded and why.

@coolmacool

Copy link
Copy Markdown
Author

I have read the IPR Policy

@aao-ipr-bot

aao-ipr-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

IPR Policy — signed

Thanks, @coolmacool. Your agreement to the IPR Policy is recorded at signatures/ipr-signatures.json and applies to all AAO repositories.

aao-ipr-bot Bot pushed a commit that referenced this pull request Aug 4, 2026
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.

Bug: Daily Addie conversation limit not resetting after 24 hours

1 participant