Skip to content

fix(api): skip the shared rate-limit budget when chat Q&A is off for the repo - #9734

Merged
JSONbored merged 1 commit into
JSONbored:mainfrom
shin-core:fix/chat-qa-skip-ratelimit-when-disabled-9714
Jul 29, 2026
Merged

fix(api): skip the shared rate-limit budget when chat Q&A is off for the repo#9734
JSONbored merged 1 commit into
JSONbored:mainfrom
shin-core:fix/chat-qa-skip-ratelimit-when-disabled-9714

Conversation

@shin-core

Copy link
Copy Markdown
Contributor

What & why

Closes #9714.

POST /v1/repos/:owner/:repo/pulls/:number/chat-qa 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.
On a repo where chat Q&A is not enabled, generateChatQaAnswer 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 a UI/script hitting such a repo could drive the shared
counter to its maxPerWindow, throttling the maintainer's genuine @loopover chat usage on that PR.

Change

Gate the route on isRepoChatQaEnabled(settings) — the predicate the maintainer dashboard's
capability map already reads (routes.ts:1853), so the dashboard flag and the route's gate can never
disagree — right after resolving settings. When it returns false, return generateChatQaAnswer's
own disabled result (its bundle is unused on that short-circuit 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 src/services/ai-chat-qa.ts is not touched.

Validation

  • New test: a disabled repo returns { status: "disabled" } (200) and writes no
    COMMAND_RATE_LIMIT_EVENT_TYPE audit row (asserts the audit_events count for the targetKey is 0).
  • New test: that same disabled request never calls planNextWork (module-mocked, asserts zero calls).
  • Existing enabled-path regression tests (rate-limit counting, the audit row under
    commandRateLimitPolicy: "hold", and the rate_limited response over maxPerWindow) pass unchanged.
  • Bug-catch verified: removing the short-circuit fails the new no-planNextWork / no-audit-row tests.
  • 100% patch coverage on both branches of the new gate.

One existing enabled-path test that had never stubbed the chatQa-enabling .loopover.yml manifest
(and only passed because the route previously ran unconditionally) now enables it explicitly, so it
genuinely exercises the enabled path it describes.

…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
@shin-core
shin-core requested a review from JSONbored as a code owner July 29, 2026 05:28
@superagent-security

Copy link
Copy Markdown
Contributor

🚨 Contributor flagged. Click here for more info: Superagent Dashboard

@superagent-security superagent-security Bot added the contributor:flagged Contributor flagged for review by trust analysis. label Jul 29, 2026
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-29 05:37:26 UTC

2 files · 1 AI reviewer · no blockers · CI green · unstable

⏸️ Suggested Action - Manual Review

Review summary
This gates POST /v1/repos/:owner/:repo/pulls/:number/chat-qa on isRepoChatQaEnabled(settings) immediately after resolving settings, short-circuiting to generateChatQaAnswer's own disabled result before the shared rate-limit counter is incremented and before planNextWork builds the grounding bundle. The predicate is the same one the maintainer dashboard capability map already reads (isRepoChatQaEnabled, already imported in routes.ts), so the dashboard flag and the route gate stay in sync. Two new tests directly assert the fix: zero audit_events rows for the targetKey and zero planNextWork calls on a disabled repo, plus an update to the existing null-author test to explicitly enable chatQa now that the disabled short-circuit exists earlier in the flow.

Nits — 4 non-blocking
  • src/api/routes.ts: the new block re-derives `resolveChatQaActor(gate.identity)` inline, and then the pre-existing `const actor = resolveChatQaActor(gate.identity);` a few lines below computes it again on the enabled path — harmless (cheap, pure) but slightly redundant; consider hoisting the actor resolution above the gate if it doesn't affect early-return semantics.
  • The disabled branch passes `bundle: null` to generateChatQaAnswer and the PR description asserts the bundle is unused on that path — this repo's ai-chat-qa.ts isn't in the diff or provided context, so that behavior can't be independently verified here, only taken on the author's word backed by the new tests' outcome assertions.
  • src/api/routes.ts: consider extracting the disabled-response construction (question/advisoryAiRouting/repoFullName/issueNumber/actor/route fields) into a small local helper if this shape is duplicated elsewhere in the route to avoid drift between the short-circuit and the main call site further down.
  • test/unit/maintainer-chat-qa.test.ts: the new tests are well-targeted (asserting absence of audit rows and absence of planNextWork calls) — no changes needed there.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9714
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 38 registered-repo PR(s), 26 merged, 0 issue(s).
Contributor context ✅ Confirmed Gittensor contributor shin-core; Gittensor profile; 38 PR(s), 0 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Linked issue satisfaction

Addressed
The diff adds the isRepoChatQaEnabled(settings) short-circuit before the rate-limit/audit block and planNextWork, returning generateChatQaAnswer's own disabled result, exactly as required, and includes new tests verifying no audit row is written and planNextWork is not called for a disabled repo, while leaving ai-chat-qa.ts and the enabled path untouched.

Review context
  • Author: shin-core
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is registered but has no active allocation in the current snapshot.
  • Public profile languages: TypeScript, JavaScript, Solidity, Dart, Python, CSS, PHP, Rust
  • Official Gittensor activity: 38 PR(s), 0 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Await review-lane availability.
  • Then work through the remaining 1 step in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask 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.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Decision record
  • action: hold · clause: success
  • config: 90dadbc6a387296031adfc6cd80618df687f7fe937ccab4b0c8ee7a43de21f0d · pack: oss-anti-slop · ci: passed
  • record: 9b1bf9a597b6223610d901f9f59bf6238a9942b76880bf4c3dfc1ef876555fc2 (schema v5, head 47344b6)

🟩 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.

  • Re-run LoopOver review

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.35%. Comparing base (f36dc51) to head (47344b6).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

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     
Flag Coverage Δ
backend 95.58% <100.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/api/routes.ts 95.58% <100.00%> (+<0.01%) ⬆️

... and 755 files with indirect coverage changes

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 29, 2026
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Held for manual review: the gate and required CI are green, but GitHub reports this pull request's mergeable state as unstable because a non-required check or status is not passing, so LoopOver will not auto-merge. A maintainer can resolve the failing check or review and merge manually. This is an automated maintenance action.

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 29, 2026
@JSONbored
JSONbored merged commit 0f4276d into JSONbored:main Jul 29, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor:flagged Contributor flagged for review by trust analysis. gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

api(chat-qa): the route spends the shared rate-limit budget when chat Q&A is off

2 participants