fix(github): preserve Tasks outage attribution

This commit is contained in:
Brennan Benson
2026-07-16 19:16:36 -07:00
parent 9b82183d4c
commit eba96b6e10
5 changed files with 81 additions and 15 deletions
@@ -228,6 +228,22 @@ describe('GitHub issue source split', () => {
)
})
it.each(['is:issue', 'is:pr'])(
'propagates GitHub outages for scoped %s queries',
async (query) => {
getIssueOwnerRepoMock.mockResolvedValueOnce({ owner: 'stablyai', repo: 'orca' })
getOwnerRepoMock.mockResolvedValueOnce({ owner: 'fork', repo: 'orca' })
ghExecFileAsyncMock.mockRejectedValueOnce(new Error('HTTP 503: Service Unavailable'))
await expect(listWorkItems('/repo-root', 10, query)).rejects.toThrow(
'HTTP 503: Service Unavailable'
)
// The outage signal reuses the failed request; it must not add retry subprocesses.
expect(ghExecFileAsyncMock).toHaveBeenCalledTimes(1)
}
)
it("uses upstream for recent PRs when preference='upstream'", async () => {
resolveIssueSourceMock.mockResolvedValueOnce({
source: { owner: 'stablyai', repo: 'orca' },
+11
View File
@@ -1288,6 +1288,11 @@ async function listQueriedWorkItems(
}
} catch (err) {
const stderr = err instanceof Error ? err.message : String(err)
if (!prScope && classifyGitHubUnavailable(stderr)) {
// Why: a scoped issue query has no successful side to preserve. Let the
// cross-repo caller attribute the empty result to GitHub instead.
throw err
}
return { items: [], issuesError: classifyListIssuesError(stderr) }
}
})()
@@ -1320,6 +1325,12 @@ async function listQueriedWorkItems(
return hydrated
} catch (err) {
console.warn('listQueriedWorkItems PRs partial failure:', err)
const stderr = err instanceof Error ? err.message : String(err)
if (!issueScope && classifyGitHubUnavailable(stderr)) {
// Why: Tasks scopes every query to one work-item kind. Propagate an
// outage instead of converting the only failed source into an empty list.
throw err
}
return []
}
})()