Skip to content

feat(plan): per-account limit overrides#33

Open
t03i wants to merge 1 commit into
mainfrom
feat/per-account-limit
Open

feat(plan): per-account limit overrides#33
t03i wants to merge 1 commit into
mainfrom
feat/per-account-limit

Conversation

@t03i

@t03i t03i commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Summary

Splits the plan enum into a scheduling class (queue priority + SLO bucket) and per-account numeric quota, so two accounts on the same class can enforce different limits — the original intent behind "enterprise".

PlanResolver.resolve now returns ResolvedAccount { plan, limits }, merging a sparse user.limits jsonb override over plan-class defaults composed from the existing config sources (PLAN_LIMITS, RATE_LIMIT_SUBMISSIONS_*, shedding sloSeconds). Consumers read auth.limits.* off the auth context instead of indexing PLAN_LIMITS[plan] at the call site.

Changes

  • shared: EffectiveLimits / ResolvedAccount, OverrideLimitsSchema (.strict(), capped positive ints), mergeLimits, MAX_SEQUENCE_LENGTH_CAP.
  • gateway: DbPlanResolver reads plan, limits and parses the override defensively (parse failure → logs + class defaults); AuthContext carries limits.
  • consumers: rate limiter (submissionsPerMinute), withinConcurrentJobLimit (maxConcurrentJobs), per-request maxSequenceLength check in both submit routes, and shedding's decideAdmission (per-account sloSeconds, falling back to config).
  • persistence: additive nullable user.limits migration (no backfill) + dev seed example.
  • admin: PUT/DELETE /admin/accounts/{userId}/limits (full-replace / clear-to-NULL), audit-logged, behind the existing admin gate.
  • spec: archived the completed per-account-limit-overrides change → canonical plan-limits spec; folded prod shedding-validation details (enforce toggle, configurability round-trip, per-account sloSeconds knob) into simplify-load-testing.

Behaviour

Defaults equal current behaviour — deploying with zero overrides is a no-op. The static submit schema .max was raised to MAX_SEQUENCE_LENGTH_CAP so an override can actually raise the length ceiling; the resolved per-account limit is enforced in the handler. openapi.v1.json reflects the new maxLength.

Testing

  • bun run typecheck / lint / test (598 unit + component) / build — all green.
  • Backend E2E (full stack): 19/19 pass, including new limits-override.test.ts proving an override enforces a limit differing from the plan default; migration applied the column live.

🤖 Generated with Claude Code

Split the `plan` enum into a scheduling class (priority + SLO bucket)
and per-account numeric quota. `PlanResolver.resolve` now returns
`ResolvedAccount { plan, limits }`, merging a sparse `user.limits jsonb`
override over plan-class defaults composed from existing config sources.

- shared: `EffectiveLimits`/`ResolvedAccount`, `OverrideLimitsSchema`
  (strict, capped positive ints), `mergeLimits`, `MAX_SEQUENCE_LENGTH_CAP`
- gateway: `DbPlanResolver` reads `plan, limits` and parses defensively
  (parse failure → class defaults); `AuthContext` carries `limits`
- consumers read `auth.limits.*`: submissions/min, concurrency,
  per-request maxSequenceLength, and shedding's per-account sloSeconds
- additive nullable `user.limits` migration + dev seed example
- admin-only `PUT/DELETE /admin/accounts/{userId}/limits` (full-replace /
  clear), audit-logged, behind the existing admin gate
- backend E2E proving an override enforces limits differing from the plan

Archive the completed change; create the canonical `plan-limits` spec.
Fold the prod shedding-validation details (enforce toggle, configurability
round-trip, per-account sloSeconds knob) into `simplify-load-testing`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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