GitHub Traffic Control
Central gateway plan and current implementation for rate-limit-aware GitHub API traffic from Issue Reporter, Hub approvals, and Agent Orchestrator.
This page describes how Clapilot routes GitHub REST API traffic and how rate limits are handled. It is for developers touching Issue Reporter, Hub issue approval, or Agent Orchestrator GitHub calls. Part of this design is shipped (the shared githubTrafficFetch helper and the write queue table); the gateway budgets, queue drain worker, and shared webhook ingestion are still planned and are marked as such below.
Architecture Decision
Clapilot will route internal GitHub REST API calls through a central GitHub Traffic Control layer. The first implementation scope is intentionally narrow:
- normalize GitHub rate-limit detection for Issue Reporter and Hub approval writes
- log GitHub rate-limit headers from the shared web helper
- queue Hub approval issue-creation writes when GitHub returns a primary or secondary rate limit
- document the current GitHub call inventory and rollout path for Agent Orchestrator traffic
The long-term gateway owns request budgeting, read caching, write queues, and webhook ingestion. Existing Agent Orchestrator code already has a local githubApiFetch wrapper with cooldown and poll caches; that should be folded into the shared gateway instead of expanded as a second permanent abstraction.
Current Call Inventory
| Area | Current path | Traffic type | Notes |
|---|---|---|---|
| Issue Reporter | src/lib/issue-reporter.ts | POST /repos/{owner}/{repo}/issues | Now uses the shared githubTrafficFetch helper for header logging and rate-limit errors. |
| Hub Approval | src/app/api/hub/issues/[id]/route.ts -> createGithubIssueFromHubReport | user-triggered write | Rate-limited writes are inserted into github_traffic_control_queue and surfaced as queued instead of generic 502. |
| Agent Orchestrator repo list | bundled-modules/agent-orchestrator/api/handler.mjs | user-visible read | Uses local repo-list cache keyed by token hash. |
| PR Review automation | bundled-modules/agent-orchestrator/api/handler.mjs | background reads and review writes | Uses local githubApiFetch; should migrate to shared gateway in the next implementation slice. |
| Issue Observer | bundled-modules/agent-orchestrator/api/handler.mjs | background reads, comments, issue state writes | Poll caches already exist for default branch, open issues, and open PRs. |
| Mention Observer | bundled-modules/agent-orchestrator/api/handler.mjs and github-mention-observer.mjs | background reads, reaction/comment writes | Candidate detection polls issues, PRs, comments, reviews, and check data. |
| Tracked PR follow-up | bundled-modules/agent-orchestrator/api/handler.mjs and tracked-pr-followup.mjs | background reads | Reads comments, reviews, check-runs, and PR state for follow-up decisions. |
| CI preview cleanup | scripts/ci/cleanup-pr-preview.sh | CI-only read | Stays outside product gateway unless moved into long-running app automation. |
Gateway Contract
Every product GitHub request should record:
- method and normalized endpoint
- actor, for example
issue_reporter,hub_approval,agent_orchestrator_pr_review, orsupervisor - token or installation identity without storing the secret
x-ratelimit-limit,x-ratelimit-remaining,x-ratelimit-reset,x-ratelimit-used,x-ratelimit-resourcex-github-request-id- response status and rate-limit classification
Rate-limit errors must map to a user-facing 429 or queued response, not a generic 502. The message should include the reset timestamp and GitHub request id when available.
Queue
github_traffic_control_queue stores write work that should not be dropped on rate limit. The first queued action is hub_issue_create; it records the Hub issue id and reviewer in the JSON payload with status waiting_for_rate_limit.
Queue states are:
pendingwaiting_for_rate_limitfailed_permanentcompleted
Status: enqueueing works today (enqueueGithubWrite in src/lib/github-traffic-control.ts), but the drain worker is not implemented yet. A follow-up worker should drain due rows by priority, call the same gateway, increment attempts, and mark permanent failures only after non-rate-limit errors or an attempt cap. Until that worker exists, queued rows stay in the table and need manual follow-up.
Request Budgets
Priority order:
- user-triggered writes
- active coding/debug jobs
- user-visible reads
- background sync and supervisor polling
When remaining budget is low, background polling should skip or extend its interval before user-triggered actions are affected. Agent Orchestrator already caches some poll reads; the next slice should add a shared budget check before runSymphonyPollTick scans GitHub repos.
Read Caching Strategy
Recommended cache windows:
- repo metadata: 10 to 60 minutes
- issue and PR metadata: 60 to 120 seconds
- check status: 30 to 90 seconds
- comments and reviews: 60 to 180 seconds
- workflow logs: explicit request only, or fetched when a failed check requires diagnostics
In-flight request coalescing should key by token identity, method, endpoint, and query params so parallel agents share the same read result.
Webhook Follow-up
Today, GitHub webhooks are received only by the Agent Orchestrator module (public module endpoint /api/modules/agent-orchestrator/webhook); there is no shared product-level webhook route yet. The planned shared gateway should add a GitHub webhook endpoint for:
issuesissue_commentpull_requestpull_request_reviewcheck_runcheck_suiteworkflow_run
Webhook events should invalidate gateway caches and enqueue agent work. Polling remains a fallback and supervisor recovery path, but it should not be the primary freshness mechanism for active repositories. Agent Orchestrator now also uses completed failing workflow_run, check_run, and check_suite webhook payloads to start a configured default-branch CI fix run that opens a PR, while PR-branch failures continue through tracked PR follow-up.
