fix(api): skip the shared rate-limit budget when chat Q&A is off for the repo - #9734
Conversation
…the repo The chat-qa route counted a prior invocation and recorded a COMMAND_RATE_LIMIT_EVENT_TYPE audit row (the SAME counter the @Loopover chat PR-comment command uses), then built a planNextWork grounding bundle, before calling generateChatQaAnswer -- which, on a repo where chat Q&A is not enabled, is guaranteed to return `disabled`. So every request to a chat-disabled repo spent a slot in the shared budget and paid for the grounding bundle for an answer that never came, and could drive the maintainer's genuine @Loopover chat usage on that PR to its limit. Gate the route on isRepoChatQaEnabled(settings) -- the same predicate the maintainer dashboard's capability map already reads, so the two can never disagree -- immediately after resolving settings. When it is false, return generateChatQaAnswer's own disabled result (its bundle is unused on that path) before the rate-limit block and before planNextWork. The enabled path -- counting, the audit row, planNextWork, and the rate_limited response -- is unchanged, and ai-chat-qa.ts is not touched. Adds tests asserting a disabled repo writes no audit row and never calls planNextWork, while the existing enabled-path rate-limit/audit/rate_limited tests continue to pass. One existing enabled-path test that had never stubbed the chatQa-enabling manifest (and only passed because the route used to run unconditionally) now enables it explicitly. Closes JSONbored#9714
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-29 05:37:26 UTC
Review summary Nits — 4 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Decision record
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9734 +/- ##
===========================================
- Coverage 90.27% 77.35% -12.92%
===========================================
Files 904 283 -621
Lines 113168 61934 -51234
Branches 26835 9267 -17568
===========================================
- Hits 102162 47912 -54250
- Misses 9676 13651 +3975
+ Partials 1330 371 -959
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Held for manual review: the gate and required CI are green, but GitHub reports this pull request's mergeable state as |
What & why
Closes #9714.
POST /v1/repos/:owner/:repo/pulls/:number/chat-qacounted a prior invocation and recorded aCOMMAND_RATE_LIMIT_EVENT_TYPEaudit row — the same counter the@loopover chatPR-commentcommand uses — then built a
planNextWorkgrounding bundle, before callinggenerateChatQaAnswer.On a repo where chat Q&A is not enabled,
generateChatQaAnsweris guaranteed to returndisabled. Soevery request to a chat-disabled repo spent a slot in the shared budget and paid for the grounding
bundle for an answer that never came — and a UI/script hitting such a repo could drive the shared
counter to its
maxPerWindow, throttling the maintainer's genuine@loopover chatusage on that PR.Change
Gate the route on
isRepoChatQaEnabled(settings)— the predicate the maintainer dashboard'scapability map already reads (
routes.ts:1853), so the dashboard flag and the route's gate can neverdisagree — right after resolving settings. When it returns
false, returngenerateChatQaAnswer'sown disabled result (its
bundleis unused on that short-circuit path) before the rate-limitblock and before
planNextWork. The enabled path — counting, the audit row,planNextWork, andthe
rate_limitedresponse — is unchanged, andsrc/services/ai-chat-qa.tsis not touched.Validation
{ status: "disabled" }(200) and writes noCOMMAND_RATE_LIMIT_EVENT_TYPEaudit row (asserts theaudit_eventscount for the targetKey is 0).planNextWork(module-mocked, asserts zero calls).commandRateLimitPolicy: "hold", and therate_limitedresponse overmaxPerWindow) pass unchanged.One existing enabled-path test that had never stubbed the chatQa-enabling
.loopover.ymlmanifest(and only passed because the route previously ran unconditionally) now enables it explicitly, so it
genuinely exercises the enabled path it describes.