Problem
/api/pr-followup/sync reports "PR follow-up sync complete" with a success response even when most repo fetches failed. Per-repo fetchPullRequests failures are caught, console.error'd, and continue'd — invisible to the caller and to any monitoring.
Observed 2026-07-29: with the GitHub token rate-limited, a forced sync returned {reposScanned: 8, prsScanned: 5, enqueued: 1} when ~29 open bot PRs existed. Conflicted PRs (e.g. miso-chat#723) silently never enqueued, and the operator's only clue was "the queue looks thin." The in-app scheduler runs this every 15 minutes, so a rate-limited token turns the entire PR-followup pipeline off with no signal.
Proposed fix
- Count fetch failures and include them in the response:
{reposScanned, reposFailed, prsScanned, enqueued, skipped}.
- Log a WARN (not just console.error) when
reposFailed > 0, including the error class (rate-limit 403 vs other).
- Optional: surface a
pr_followup_last_success / repos_failed gauge for alerting, and skip-with-backoff when the rate-limit reset header indicates the window is exhausted (avoid burning the remaining budget on doomed calls).
Acceptance
- A sync where every repo 403s returns a response that visibly says so, and the log carries a WARN.
- Rate-limited syncs don't spend remaining API budget on per-PR detail calls once the window is known-exhausted.
Problem
/api/pr-followup/syncreports"PR follow-up sync complete"with a success response even when most repo fetches failed. Per-repofetchPullRequestsfailures are caught,console.error'd, andcontinue'd — invisible to the caller and to any monitoring.Observed 2026-07-29: with the GitHub token rate-limited, a forced sync returned
{reposScanned: 8, prsScanned: 5, enqueued: 1}when ~29 open bot PRs existed. Conflicted PRs (e.g. miso-chat#723) silently never enqueued, and the operator's only clue was "the queue looks thin." The in-app scheduler runs this every 15 minutes, so a rate-limited token turns the entire PR-followup pipeline off with no signal.Proposed fix
{reposScanned, reposFailed, prsScanned, enqueued, skipped}.reposFailed > 0, including the error class (rate-limit 403 vs other).pr_followup_last_success/repos_failedgauge for alerting, and skip-with-backoff when the rate-limit reset header indicates the window is exhausted (avoid burning the remaining budget on doomed calls).Acceptance