Summary
recent_merged_pull_requests is the only segment that uses progressiveHistory, and it is the only segment whose terminal status is sampled. But sampled is not a resumable status, and nothing ever dispatches this segment in resume mode — so the nextCursor the segment faithfully records is written and never read. "Progressive history" never progresses: the crawl can only ever re-cover the first SEGMENT_PAGE_BUDGET pages of /pulls?state=closed&sort=updated&direction=desc, no matter how many times it runs.
Found while recovering the merged-PR history frozen by #10193.
The three gates, all in src/github/backfill.ts
progressiveHistory maps complete && hasMore to sampled (never running), so the status that would normally mean "more pages pending" is never produced for this segment.
canResumePreviousScan requires previous.status to be one of running / partial / waiting_rate_limit. sampled is absent, so both previous.nextCursor and an explicitly passed cursor are ignored and startPage falls back to 1.
- The automatic
mode: "resume" re-send at src/github/backfill.ts:625 is explicitly scoped to labels / open_issues / open_pull_requests — recent_merged_pull_requests is excluded — and the scheduled cron only ever dispatches light or full.
So there is no path, scheduled or manual, that advances this segment past page SEGMENT_PAGE_BUDGET[mode].
Verified on edge-nl-01
The segment for JSONbored/awesome-claude sitting at status=sampled, last_cursor=10, next_cursor=11, given a resume run with an explicit cursor: "11":
POST /v1/internal/jobs/backfill-repo-segment/run
{"segment":"recent_merged_pull_requests","mode":"resume","cursor":"11","force":true}
{"status":"sampled","fetchedCount":1758,"expectedCount":3588,"nextCursor":"11"}
fetchedCount unchanged at 1758, nextCursor still 11 — it re-crawled pages 1–10 and persisted nothing new. expectedCount is 3588, so less than half the merged history is reachable, and the unreachable half is unreachable permanently.
Consequence
The segment is not a progressive historical crawl; it is a rolling top-N-by-updated sample that grows only by accretion as the window slides, and is then trimmed by the 30-day updated_at retention on the same table (src/db/retention.ts:97). Its steady state is "the union of crawl windows over the last 30 days" — which is a defensible design, but it is not what progressiveHistory, nextCursor, and expectedCount describe.
Concretely, after #10203 restores the writer, the 2026-07-10 → 2026-07-23 hole that #10193 opened (~1,700 PRs on JSONbored/loopover alone) cannot be backfilled by any supported path, because those PRs' updated_at will never re-enter the top-SEGMENT_PAGE_BUDGET pages.
This is not currently user-visible, which is why it is filed separately rather than folded into #10203: every consumer reads through listRecentMergedPullRequests, which is ORDER BY merged_at DESC LIMIT 200. On JSONbored/loopover that window is presently 2026-07-29 → 2026-07-31 and fully populated. The defect is latent — it bounds how much history the table can ever hold, and it makes the cursor/expectedCount bookkeeping misleading to anyone reasoning about coverage.
Options
- Add
sampled to canResumePreviousScan's accepted statuses and dispatch a resume follow-on for this segment (bounded, so a deep history is walked over several runs rather than one). This makes the machinery do what its own field names claim.
- Or accept the rolling-sample design and remove the misleading parts: stop recording a
nextCursor that can never be consumed, and document the segment as a bounded recent-window sample.
Either is defensible; option 1 matches the naming and the stored expectedCount, option 2 is the smaller change. Worth a maintainer decision before anyone implements.
Suitable for a contributor if relabelled — it is well-scoped and fully testable against createTestEnv().
Summary
recent_merged_pull_requestsis the only segment that usesprogressiveHistory, and it is the only segment whose terminal status issampled. Butsampledis not a resumable status, and nothing ever dispatches this segment inresumemode — so thenextCursorthe segment faithfully records is written and never read. "Progressive history" never progresses: the crawl can only ever re-cover the firstSEGMENT_PAGE_BUDGETpages of/pulls?state=closed&sort=updated&direction=desc, no matter how many times it runs.Found while recovering the merged-PR history frozen by #10193.
The three gates, all in
src/github/backfill.tsprogressiveHistorymapscomplete && hasMoretosampled(neverrunning), so the status that would normally mean "more pages pending" is never produced for this segment.canResumePreviousScanrequiresprevious.statusto be one ofrunning/partial/waiting_rate_limit.sampledis absent, so bothprevious.nextCursorand an explicitly passedcursorare ignored andstartPagefalls back to 1.mode: "resume"re-send atsrc/github/backfill.ts:625is explicitly scoped tolabels/open_issues/open_pull_requests—recent_merged_pull_requestsis excluded — and the scheduled cron only ever dispatcheslightorfull.So there is no path, scheduled or manual, that advances this segment past page
SEGMENT_PAGE_BUDGET[mode].Verified on edge-nl-01
The segment for
JSONbored/awesome-claudesitting atstatus=sampled, last_cursor=10, next_cursor=11, given aresumerun with an explicitcursor: "11":fetchedCountunchanged at 1758,nextCursorstill11— it re-crawled pages 1–10 and persisted nothing new.expectedCountis 3588, so less than half the merged history is reachable, and the unreachable half is unreachable permanently.Consequence
The segment is not a progressive historical crawl; it is a rolling top-N-by-
updatedsample that grows only by accretion as the window slides, and is then trimmed by the 30-dayupdated_atretention on the same table (src/db/retention.ts:97). Its steady state is "the union of crawl windows over the last 30 days" — which is a defensible design, but it is not whatprogressiveHistory,nextCursor, andexpectedCountdescribe.Concretely, after #10203 restores the writer, the 2026-07-10 → 2026-07-23 hole that #10193 opened (~1,700 PRs on
JSONbored/loopoveralone) cannot be backfilled by any supported path, because those PRs'updated_atwill never re-enter the top-SEGMENT_PAGE_BUDGETpages.This is not currently user-visible, which is why it is filed separately rather than folded into #10203: every consumer reads through
listRecentMergedPullRequests, which isORDER BY merged_at DESC LIMIT 200. OnJSONbored/loopoverthat window is presently 2026-07-29 → 2026-07-31 and fully populated. The defect is latent — it bounds how much history the table can ever hold, and it makes the cursor/expectedCount bookkeeping misleading to anyone reasoning about coverage.Options
sampledtocanResumePreviousScan's accepted statuses and dispatch aresumefollow-on for this segment (bounded, so a deep history is walked over several runs rather than one). This makes the machinery do what its own field names claim.nextCursorthat can never be consumed, and document the segment as a bounded recent-window sample.Either is defensible; option 1 matches the naming and the stored
expectedCount, option 2 is the smaller change. Worth a maintainer decision before anyone implements.Suitable for a contributor if relabelled — it is well-scoped and fully testable against
createTestEnv().