fix(github): count gh pr and issue commands against the GraphQL rate limit (#28778)

* fix(github): count gh pr and issue commands against the GraphQL rate limit

gh serves pr/issue/repo view-style subcommands over GraphQL, but the breaker
filed every non-api command under core and only matched REST's
"API rate limit exceeded" wording, so GraphQL exhaustion never tripped it.
Map those subcommands to the graphql bucket, match GraphQL's
"API rate limit already exceeded", and guard project-view issue close/reopen
against the GraphQL quota.

Fixes #28767

* test(github): drop redundant Date.now() args in graphql breaker test

Keeps the flaky-test-pattern ratchet at its baseline.

---------

Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
Žan Mlakar
2026-10-27 22:57:10 -07:00
committed by GitHub
co-authored by Neil
parent 9105ade917
commit 4dca163bb7
6 changed files with 68 additions and 28 deletions
@@ -111,7 +111,7 @@ describe('updateIssueBySlug', () => {
expect(runRestMock).not.toHaveBeenCalled()
})
it('guards and accounts state mutations in the selected host quota scope', async () => {
it('guards and accounts state mutations in the selected host GraphQL quota', async () => {
const args = {
owner: 'acme',
repo: 'widgets',
@@ -122,8 +122,8 @@ describe('updateIssueBySlug', () => {
await expect(updateIssueBySlug(args)).resolves.toEqual({ ok: true })
expect(repositoryRateLimitGuardMock).toHaveBeenCalledWith(args, 'core')
expect(noteRepositoryRateLimitSpendMock).toHaveBeenCalledWith(args, 'core')
expect(repositoryRateLimitGuardMock).toHaveBeenCalledWith(args, 'graphql')
expect(noteRepositoryRateLimitSpendMock).toHaveBeenCalledWith(args, 'graphql')
})
it('does not spawn a state mutation while that host quota is blocked', async () => {
+2 -2
View File
@@ -107,7 +107,7 @@ async function updateIssueState(args: UpdateIssueBySlugArgs): Promise<GitHubProj
if (state === undefined) {
return { ok: true }
}
const guard = repositoryRateLimitGuard(args, 'core')
const guard = repositoryRateLimitGuard(args, 'graphql')
if (guard.blocked) {
return { ok: false, error: rateLimitedError(guard) }
}
@@ -125,7 +125,7 @@ async function updateIssueState(args: UpdateIssueBySlugArgs): Promise<GitHubProj
}
}
await acquire()
noteRepositoryRateLimitSpend(args, 'core')
noteRepositoryRateLimitSpend(args, 'graphql')
try {
await ghExecFileAsync(stateArgs, {
encoding: 'utf-8',