Skip to content

fix(web): stop workflow status polls draining the shared AI rate-limit bucket - #1518

Merged
groupthinking merged 3 commits into
mainfrom
claude/clever-heisenberg-3rd3sh
Aug 13, 2026
Merged

fix(web): stop workflow status polls draining the shared AI rate-limit bucket#1518
groupthinking merged 3 commits into
mainfrom
claude/clever-heisenberg-3rd3sh

Conversation

@groupthinking

@groupthinking groupthinking commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Canonical issue

Closes #1517

Outcome

Studio's Act on findings can complete a run instead of 429ing partway through it, and doing so no longer takes /api/chat and /api/transcribe down as collateral.

isAiRoute is now method-aware. GET/HEAD on /api/workflows falls to the general budget (60/min); POST .../video-to-actions stays AI-class because starting a run genuinely does model work.

Scope

  • Included:
    • auth-paths.tsisAiRoute(pathname, method) plus AI_ROUTE_PREFIXES, moved here from proxy.ts. This file exists as the home for path policy "free of Next.js request types so vitest can import it offline" and already hosts the sibling shouldSkipRateLimit, so the classifier becomes unit-testable without a Next.js request harness.
    • proxy.ts — imports the classifier, threads request.method through getRateLimit and the bucket-class decision. Net −18 lines.
    • studio-workflow.ts — poll cadence retuned to 30 × 2s.
    • auth-paths.test.ts — 10 new cases.
  • Explicitly excluded:
    • Per-path rate-limit buckets. The ai:<ip> / api:<ip> class keying is what makes one route's polling everyone else's problem. Splitting buckets per prefix is the more thorough fix and a larger design change; this PR corrects the classification that made it bite.
    • The AI_LIMIT default of 12/min. Unchanged and unexamined here.
    • Auth/session behaviour. needsAuthentication and the gate modes are untouched.

Risk

  • Risk level: low
  • Failure mode: the change is a widening for exactly one method/prefix pair. Worst case, a status poll is metered at 60/min instead of 12/min — it is a stored-state read that makes no model call and no provider spend. The stricter direction is preserved everywhere else, and an omitted method argument defaults to POST, so a future caller that forgets it lands on the tighter budget rather than silently widening one.
  • Rollback: revert the two commits. No migration, config, env var, schema, response shape, or status code changes.

Verification

Head 3c0d78d. Measured, not inferred.

  • Focused tests — 10 new cases in auth-paths.test.ts, all passing.

  • Non-vacuity proven by running the suite against each pre-fix baseline, not by reasoning about them:

    Baseline Result
    method-agnostic isAiRoute (the behaviour on main) 3 of 10 fail — the two poll-exemption cases and the case-insensitivity case
    segment check removed (exemption matched by loose startsWith) 1 of 10 fails — the prefix-leak case
    this head 10 pass

    The remaining 6 new cases pass on every baseline by design — they are controls. POST stays AI-class, mutating verbs stay AI-class, GET on /api/chat and /api/transcribe stays AI-class, and an omitted method still defaults strict. Without them, a "fix" that simply dropped /api/workflows from the list, or exempted GET globally, would also go green.

  • Full apps/web suite — 58 files, 355 passed, 0 failed. No regressions.

  • npx tsc --noEmit — clean

  • npm run lint — clean

  • Vercel preview — READY on this branch (gate 4).

  • Required CI — first run pending on this head.

  • Review threads resolved — none open.

These tests actually gate, which was not true a few hours ago: #1480 added the test-frontend job that runs the apps/web vitest suite in CI.

Production evidence

Not applicable as a deployed artefact. The observable effect is the absence of a 429 on a polling endpoint, and reproducing it against a preview needs provider keys plus a real YouTube URL. The rate-limit classification is pure path/method policy with no I/O, so the unit tests measure it directly — which is the reason for moving it out of proxy.ts.

Agent handoff

Note for reviewers

Three things worth flagging beyond the diff.

1. This was found on #1507 before it merged, and merged anyway. The Vercel Agent review flagged the prefix addition, and a follow-up comment quantified it. #1507 merged as 5135f4e with proxy.ts unchanged from the flagged state, so the defect is on main now rather than in review. Not a criticism of the change — it is a good feature — but the finding existed and the merge did not pick it up.

2. The poll retune is part of the same defect, not unrelated tidying. Fixing only the classifier moves the poller from 40/min against a 12/min ceiling to 40/min against a 60/min ceiling. That clears, but leaves ~20 req/min for everything else the Studio page does on the shared api:<ip> bucket — thin enough that ordinary page traffic could still 429. At 2s the run spends 30/min and leaves roughly half the allowance. The wall-clock window doubling to 60s is a side benefit: the previous 30s was shorter than a transcript fetch plus an agent call, which is why the UI's Still running after N polls branch was so easy to hit.

3. The second commit is a self-review catch, and it is the same shape as #1486's bug. The exemption originally reused the loose startsWith that class membership uses, so a future /api/workflows-admin would have inherited the GET carve-out and silently landed on the looser budget. No route in the tree does that today — I checked every directory under apps/web/src/app/api — so it was latent, not live. But "an incidental block quietly becomes an allow" is exactly what the SSRF guard had to fix in #1486, and it was cheap to close here. Class membership deliberately keeps loose matching: tightening it would move routes off the stricter budget, which is a widening this PR has no business making.

The deeper issue is that the bucket is keyed by class rather than by path, so any one AI route's traffic is charged against every other's. That is out of scope here and noted under Scope above.

…t bucket

#1507 added /api/workflows to AI_ROUTE_PREFIXES. isAiRoute classified by path
prefix alone, so the polled GET status endpoint was metered against the AI
budget (default 12/min) while pollVideoToActions polled at 40/min. The 12th
request 429'd ~17s into a 30s window, before a transcript fetch plus an agent
call could finish.

The bucket is keyed by class, not path, so every AI prefix shares one ai:<ip>
counter -- a single Studio run also 429'd /api/chat, /api/transcribe and
/api/pipeline as collateral.

Move the classifier into auth-paths.ts, which exists as the home for path
policy free of Next.js request types so vitest can import it offline, and make
it method-aware. GET/HEAD on /api/workflows falls to the general budget; POST
stays AI-class because starting a run does real model work. The exemption is
keyed per-prefix rather than exempting GET globally, so it cannot widen another
route that later serves model work over GET. An omitted method defaults to POST
so the failure mode is the stricter limit.

Also retune the poller to 30 attempts x 2s: 30 req/min leaves roughly half the
general allowance for the rest of the page, and the wall-clock window doubles to
60s, which better fits the work the run actually does.

Closes #1517
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-uvai Ready Ready Preview, v0 Aug 7, 2026 9:36pm

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@groupthinking, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 111 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8ba293b4-e5a4-4526-929b-6a923d95e21c

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc1f03 and 88ae493.

📒 Files selected for processing (4)
  • apps/web/src/lib/__tests__/auth-paths.test.ts
  • apps/web/src/lib/auth-paths.ts
  • apps/web/src/lib/studio-workflow.ts
  • apps/web/src/proxy.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@groupthinking groupthinking added bug Something isn't working javascript Pull requests that update javascript code tests labels Aug 7, 2026 — with Claude

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Auto-review skipped this PR because it had no labels — the config requires at least one of 26, and a PR opened via the API starts unlabeled. Added javascript, bug, tests and re-triggering.

Worth noting this is the failure mode MERGE_POLICY.md v2 calls out by name ("CodeRabbit skips any pull request without one of 26 labels, so an unlabeled pull request could never obtain the approval the gate demanded"). The label job does apply labels automatically, but it was still queued behind the Actions backlog when CodeRabbit evaluated the PR, so the auto-review lost the race. #1508 appears to be addressing that ordering.

Under gate 3 a config-skip would satisfy the review gate on its own, but this PR changes rate-limiting behaviour and deserves an actual read rather than a technicality.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@groupthinking I will review the rate-limit classification and workflow polling changes.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…e AI budget

Self-review follow-up. The exemption looked the prefix up with the same loose
startsWith used for class membership, so a future sibling surface whose name
merely starts with an exempted prefix -- /api/workflows-admin -- would silently
inherit the GET carve-out and drop onto the looser budget.

No route in the tree does this today (checked every directory under
apps/web/src/app/api), so this is latent rather than live. It is worth closing
while the file is open: the same shape, an incidental block quietly becoming an
allow, is what #1486 had to fix in the SSRF guard.

Class membership keeps its original loose matching. Narrowing that would move
routes off the stricter budget, which this change has no business doing; the
exemption is the widening, so only the exemption is tightened.
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 88ae493.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@groupthinking groupthinking self-assigned this Aug 13, 2026
@groupthinking
groupthinking marked this pull request as ready for review August 13, 2026 07:56
@groupthinking
groupthinking enabled auto-merge (squash) August 13, 2026 07:56
@groupthinking
groupthinking merged commit 12f5c44 into main Aug 13, 2026
3 of 6 checks passed
@groupthinking
groupthinking deleted the claude/clever-heisenberg-3rd3sh branch August 13, 2026 07:57
@linear-code

linear-code Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

GRV-412

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working javascript Pull requests that update javascript code tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio workflow status polls are metered on the AI budget and drain the shared ai:&lt;ip&gt; bucket

2 participants