Raised by an automated review on #40 and confirmed. Deferred out of that PR because the fix reaches into code the CLI shares.
Behaviour
When a WebMCP invocation is cancelled, or a newer browser request aborts the preceding fetch, the browser drops the connection but /api/scan and /api/compare keep running their GitHub calls to completion. /api/compare performs two full scans, so cancelling after the first still pays for the second.
The wasted work costs both deployment function time and GitHub API quota after the result has already been discarded.
Why it was not fixed in #40
The endpoints are synchronous def handlers running in Starlette's threadpool, and repopulse/github_client.py uses the blocking requests library. Observing a client disconnect means either converting the handlers to async def — which would block the event loop on those same calls — or threading a cancellation check through the core client, which the CLI also uses. That is a larger change than a deployment-focused PR should carry.
Scope of the impact
Bounded by one scan: 0.5 s to 7 s of function time in the timings measured on the deployment, and roughly 11 GitHub requests per scan or 21 per comparison. A serverless invocation is billed from the moment it starts, so even a complete fix only reclaims the remainder after the disconnect.
Possible directions
- Check
await request.is_disconnected() between the two scans in /api/compare — the cheapest partial win, since that is where a whole scan can be saved.
- Give
GitHubClient an optional cancellation callback checked between HTTP calls, left unset by the CLI.
- Move the web layer to an async HTTP client, keeping
requests for the CLI path.
No change to scoring, checks, report schemas, or CLI contracts is expected from any of these.
Raised by an automated review on #40 and confirmed. Deferred out of that PR because the fix reaches into code the CLI shares.
Behaviour
When a WebMCP invocation is cancelled, or a newer browser request aborts the preceding
fetch, the browser drops the connection but/api/scanand/api/comparekeep running their GitHub calls to completion./api/compareperforms two full scans, so cancelling after the first still pays for the second.The wasted work costs both deployment function time and GitHub API quota after the result has already been discarded.
Why it was not fixed in #40
The endpoints are synchronous
defhandlers running in Starlette's threadpool, andrepopulse/github_client.pyuses the blockingrequestslibrary. Observing a client disconnect means either converting the handlers toasync def— which would block the event loop on those same calls — or threading a cancellation check through the core client, which the CLI also uses. That is a larger change than a deployment-focused PR should carry.Scope of the impact
Bounded by one scan: 0.5 s to 7 s of function time in the timings measured on the deployment, and roughly 11 GitHub requests per scan or 21 per comparison. A serverless invocation is billed from the moment it starts, so even a complete fix only reclaims the remainder after the disconnect.
Possible directions
await request.is_disconnected()between the two scans in/api/compare— the cheapest partial win, since that is where a whole scan can be saved.GitHubClientan optional cancellation callback checked between HTTP calls, left unset by the CLI.requestsfor the CLI path.No change to scoring, checks, report schemas, or CLI contracts is expected from any of these.