Run (or resume) a scan. The scan proceeds through four phases, each checkpointing
the cache so an interrupted run can resume exactly where it stopped:
fetch-repos — fetch every issue of each target repo, page by page.
fetch-blockers — discover external gating blockers in issue bodies and fetch them.
fetch-comments — fetch comments for every full-scan issue.
fetch-comment-blockers — discover external gating blockers in comment text.
The cache's phase marker is consulted on entry, so a resumed scan re-enters at the
phase it left off in. The run returns early with 'rate-limited' when the client's
budget is exhausted; calling runScan again on the same cache path continues it.
Incremental re-run: when called on a cache that is already done with every target
repo complete, the cache is reset to fetch-repos and each repo's lastScanCompletedAt
is passed as the GraphQL since filter so only issues changed since the last run are
fetched. Additionally, before opening the page loop for any previously-complete repo,
a cheap repository.updatedAt probe (costs one rate-limit point) short-circuits the
repo entirely when GitHub reports it has not changed since lastScanCompletedAt.
New target on a done cache: when the cache is done but a named target is not
already complete (e.g. a repo added to a finished cache), phase 1 is reopened and only
the missing repos are fetched — the already-complete repos are left untouched. (Earlier
this case silently fetched nothing.)
In-memory scans: pass opts.cacheIO (a held cache + no-op save) to run the whole
pipeline without disk I/O; read the populated cache back from the held object afterwards.
This is how the cache-free all/auto one-shot path scans straight into a render.
Incremental since even mid-scan: whenever the cache already contains issues for a
repo, the max updatedAt across those issues is passed as the since filter — so an
interrupted scan resumes by skipping issues it already saw and only re-fetching ones
the GraphQL server reports as updated more recently. GraphQL's filterBy.since is
inclusive, so the boundary issue overlaps by one row (harmless).
--wait mode: when opts.wait is true, rate-limit pauses sleep until the limit
resets (using the resetAt timestamp from the response) and then continue, rather than
returning 'rate-limited'.
Run (or resume) a scan. The scan proceeds through four phases, each checkpointing the cache so an interrupted run can resume exactly where it stopped:
The cache's
phasemarker is consulted on entry, so a resumed scan re-enters at the phase it left off in. The run returns early with 'rate-limited' when the client's budget is exhausted; callingrunScanagain on the same cache path continues it.Incremental re-run: when called on a cache that is already
donewith every target repocomplete, the cache is reset tofetch-reposand each repo'slastScanCompletedAtis passed as the GraphQLsincefilter so only issues changed since the last run are fetched. Additionally, before opening the page loop for any previously-complete repo, a cheaprepository.updatedAtprobe (costs one rate-limit point) short-circuits the repo entirely when GitHub reports it has not changed sincelastScanCompletedAt.New target on a done cache: when the cache is
donebut a named target is not alreadycomplete(e.g. a repo added to a finished cache), phase 1 is reopened and only the missing repos are fetched — the already-complete repos are left untouched. (Earlier this case silently fetched nothing.)In-memory scans: pass
opts.cacheIO(a held cache + no-op save) to run the whole pipeline without disk I/O; read the populated cache back from the held object afterwards. This is how the cache-freeall/autoone-shot path scans straight into a render.Incremental
sinceeven mid-scan: whenever the cache already contains issues for a repo, the maxupdatedAtacross those issues is passed as thesincefilter — so an interrupted scan resumes by skipping issues it already saw and only re-fetching ones the GraphQL server reports as updated more recently. GraphQL'sfilterBy.sinceis inclusive, so the boundary issue overlaps by one row (harmless).--waitmode: whenopts.waitistrue, rate-limit pauses sleep until the limit resets (using theresetAttimestamp from the response) and then continue, rather than returning'rate-limited'.