Context
fanOutAgentRegateSweepJobs (src/queue/processors.ts, invoked from the agent-regate-sweep queue handler armed by the */2 * * * * cron in wrangler.jsonc) iterates every review-active repo in a plain for...of loop, awaiting resolveRepositorySettings and getLatestRegatedAt sequentially inside the loop body — both awaits block the next repo's iteration. resolveRepositorySettings alone issues 3 parallel round-trips per call, so total latency scales as roughly 4 x round-trip-latency x repoCount, entirely serialized, before the actual per-repo dispatch (which correctly uses Promise.all) even starts.
This runs unconditionally every 2 minutes for every registered+configured repo. The codebase already has a proven bounded-concurrency helper for exactly this shape of problem — mapWithConcurrencyLimit (src/signals/focus-manifest-loader.ts, used by loadRepoFocusManifests with a concurrency of 4) — but fanOutAgentRegateSweepJobs doesn't reuse it. As a self-host maintainer onboards more repos, this per-tick sequential prefix grows linearly and risks colliding with the Workers CPU-time budget for the cron-triggered invocation.
Requirements
- Replace the sequential
for loop with a bounded-concurrency map over the repo list, reusing mapWithConcurrencyLimit (or the same pattern) with a concurrency of ~4-8.
- Preserve the existing per-repo try/catch isolation and the
skippedDraining/skippedErrored counters exactly as they behave today.
Acceptance criteria
- A test with N repos and artificially delayed settings/drain-check calls demonstrates wall-clock scales sublinearly (bounded by the concurrency limit) instead of linearly with repo count.
- Existing regate-sweep behavior (candidate selection, skip counters, dispatch) is unchanged for a single repo or small repo counts.
Parent: #1667
Context
fanOutAgentRegateSweepJobs(src/queue/processors.ts, invoked from theagent-regate-sweepqueue handler armed by the*/2 * * * *cron inwrangler.jsonc) iterates every review-active repo in a plainfor...ofloop, awaitingresolveRepositorySettingsandgetLatestRegatedAtsequentially inside the loop body — both awaits block the next repo's iteration.resolveRepositorySettingsalone issues 3 parallel round-trips per call, so total latency scales as roughly4 x round-trip-latency x repoCount, entirely serialized, before the actual per-repo dispatch (which correctly usesPromise.all) even starts.This runs unconditionally every 2 minutes for every registered+configured repo. The codebase already has a proven bounded-concurrency helper for exactly this shape of problem —
mapWithConcurrencyLimit(src/signals/focus-manifest-loader.ts, used byloadRepoFocusManifestswith a concurrency of 4) — butfanOutAgentRegateSweepJobsdoesn't reuse it. As a self-host maintainer onboards more repos, this per-tick sequential prefix grows linearly and risks colliding with the Workers CPU-time budget for the cron-triggered invocation.Requirements
forloop with a bounded-concurrency map over the repo list, reusingmapWithConcurrencyLimit(or the same pattern) with a concurrency of ~4-8.skippedDraining/skippedErroredcounters exactly as they behave today.Acceptance criteria
Parent: #1667