Cloudflare Workers quota policy

CareerVector ships its public surface on Cloudflare Workers. The free tier budget is 100,000 requests per day, account-wide — shared across every Worker on the account. That single number governs how we design CI, how we design the quality runner, and what we are allowed to point at deployed workers.

This is the policy referenced from CLAUDE.md §22 as the canonical rule.

The rule

No CI workflow may run e2e tests, smoke tests, or any sustained-traffic suite against a deployed Worker on push. A single Playwright suite consumes 5–15k requests; one push-triggered workflow firing 15× in a day burned 240,317 requests on a since-retired staging worker and 1027'd the entire account as collateral damage.

The rule applies to every Worker the account hosts (ui, relay, jobcache, wiki, mcp). Quota is shared. Adding a "small" cron to one Worker steals from all the others.

Allowed patterns

Pattern Why it's safe
Unit tests on every push Runs in-process, zero network
Type-check / lint on every push Static analysis, zero network
wrangler dev / Miniflare e2e on every push Local emulation, zero CF quota
Manual CircleCI only_job e2e against deployed Human in the loop, contained; runs on resource_class: corbet/unraid
Scheduled smoke ≤ 4×/day, ≤ 10 requests/run Bounded — 40 req/day is rounding error

Forbidden patterns

  • Any on: push: workflow that hits a deployed *.workers.dev URL.
  • Any cron-scheduled e2e that loops through dozens of pages or modals against deployed Workers.
  • Adding a new [triggers] crons = [...] to a Worker without explicit budget math signed off in the change.
  • Restoring push-triggered deployed-target e2e even with rate limiting. The maintenance cost (rate-limit tuning, alert noise, accidental override) outweighs the benefit.

Quality runner posture

The Rust quality runner in tools/quality/ is the enforcement seam for this policy: budget buckets cap deployed-target traffic per run, the HTTP surface records every probe into the ledger, and the runner refuses to dispatch a suite that would breach its bucket. The runner is not a CI orchestrator — CircleCI still triggers — it is the gate that keeps any orchestrator honest about quota.

Pre-flight check

Use ~/.agent/tools/cf-quota/check.mjs to inspect the day's account-wide request consumption before adding any new Worker-hitting automation. The tool reads the CF GraphQL Analytics admin API and does not itself count against quota.

node ~/.agent/tools/cf-quota/check.mjs            # today UTC
node ~/.agent/tools/cf-quota/check.mjs --since=24h # rolling 24h
node ~/.agent/tools/cf-quota/check.mjs --day=YYYY-MM-DD

If today's account total is already > 50%, defer the change.

What to do instead

  • Local: npm --prefix ui run test:qa:core or a targeted Playwright run against the auto-started local dev server. Same browser path, zero remote quota.
  • Pre-merge: run that locally and push only after green.
  • Periodic deployed sanity: a small (≤ 6-probe) canary at schedule: '0 3 * * *' — ~6 requests/day, 0.006% of cap. Bounded.

If a real preview-per-branch workflow is needed, use a separate CF account or paid Workers plan. Never the free shared bucket.

Source: wiki/content/canon/cf-workers-quota-policy.md