Context
Sub of #12186 (P1). Operator: GitHub rate limits hit more frequently. The wgr9t499x investigation attributed this to query shape, not item count — grounded here against the query files.
Findings (grounded)
- Issues are correctly delta-gated:
queries/issueQueries.mjs:42 ($since: DateTime) + :54 (filterBy: {since: $since}), driven by IssueSyncer.mjs:532. Smallest, cheapest fetch.
- PR + Discussion queries have NO delta param — only
issueQueries.mjs matches a since/filterBy grep. pullRequestQueries.mjs + discussionQueries.mjs full-scan the entire corpus every sync. The PR full-scan (~106 sequential pages per the investigation) is the dominant load on the secondary (requests/min) limit.
- Discussion is the most expensive per-page shape:
discussionQueries.mjs:102 nests replies(first: $maxReplies) under comments (~26 pts/page per the investigation).
- Rate-limit governance is issue-path-only:
rateLimit{…} + a low-remaining guard appear only in IssueSyncer.mjs (grep) — PR + Discussion runs are unguarded.
The Fix
- PR delta gating — fetch newest-first (
orderBy: {field: UPDATED_AT, direction: DESC}) and stop paginating at the first item older than metadata.lastSync. (The pullRequests connection has no server-side since filter — confirm capability at implementation; pullRequestQueries.mjs:77 already references an intended $since.) Mirror the outcome of IssueSyncer.mjs:532.
- Discussion delta gating — same newest-first + cutoff pattern in
DiscussionSyncer.
- Discussion query cost — paginate/gate the nested
replies(first:$maxReplies) (continuation query, like the issue timeline) instead of inlining replies for the full comment set every run.
- Shared rate-limit governance — lift the
rateLimit{cost,remaining,resetAt} accounting + low-remaining guard out of IssueSyncer into the shared GraphQL execution path so PR + Discussion are governed too.
Acceptance Criteria
Out of Scope
Related
- #12186 (epic). Authority:
learn/agentos/decisions/0004-github-content-architecture.md.
Retrieval Hint: grep filterBy / $since / replies( / rateLimit in ai/services/github-workflow/{queries,sync}/.
Context
Sub of #12186 (P1). Operator: GitHub rate limits hit more frequently. The wgr9t499x investigation attributed this to query shape, not item count — grounded here against the query files.
Findings (grounded)
queries/issueQueries.mjs:42($since: DateTime) +:54(filterBy: {since: $since}), driven byIssueSyncer.mjs:532. Smallest, cheapest fetch.issueQueries.mjsmatches asince/filterBygrep.pullRequestQueries.mjs+discussionQueries.mjsfull-scan the entire corpus every sync. The PR full-scan (~106 sequential pages per the investigation) is the dominant load on the secondary (requests/min) limit.discussionQueries.mjs:102nestsreplies(first: $maxReplies)under comments (~26 pts/page per the investigation).rateLimit{…}+ a low-remaining guard appear only inIssueSyncer.mjs(grep) — PR + Discussion runs are unguarded.The Fix
orderBy: {field: UPDATED_AT, direction: DESC}) and stop paginating at the first item older thanmetadata.lastSync. (ThepullRequestsconnection has no server-sidesincefilter — confirm capability at implementation;pullRequestQueries.mjs:77already references an intended$since.) Mirror the outcome ofIssueSyncer.mjs:532.DiscussionSyncer.replies(first:$maxReplies)(continuation query, like the issue timeline) instead of inlining replies for the full comment set every run.rateLimit{cost,remaining,resetAt}accounting + low-remaining guard out ofIssueSyncerinto the shared GraphQL execution path so PR + Discussion are governed too.Acceptance Criteria
lastSync, not the whole corpus (full-scan → a handful of pages).Out of Scope
Related
learn/agentos/decisions/0004-github-content-architecture.md.Retrieval Hint: grep
filterBy/$since/replies(/rateLimitinai/services/github-workflow/{queries,sync}/.