Problem
The budget Gate maintains a single REST ResourceBudget (internal/budget/gate.go), and every rate-limit observation merges into it. App-JWT traffic (webhook-delivery APIs) and installation-token traffic (repository APIs) are different authentication contexts with separate GitHub rate-limit pools (App JWT ~5,000/hour for the app; GHEC installation tokens 15,000/hour), but their x-ratelimit-* headers currently overwrite each other's shared admission state.
During the 2026-08-04 rate-limit incident, ghsync reported remaining budget and kept admission open while GitHub returned "rate limit exceeded for installation ID" — consistent with a JWT response's headers masking the exhausted installation pool.
Proposed behavior
- Key
ResourceBudget state by credential context (installation REST, App-JWT REST; GraphQL is already separate).
- Per-context admission, floor reservations, backoff, and lease-persisted snapshots; a header observation updates only its own context.
- Metrics (
ghsync_c_b3_*) gain an auth-context label so exhaustion is attributable.
Acceptance criteria
- A 403/backoff in one context does not block or misreport admission in the other.
- Observed limits per context match GitHub's actual pools; the merged-header masking failure mode has a regression test.
- Budget lease snapshot/restore round-trips both contexts.
- Conformance/loadgen coverage exercises both contexts concurrently.
Refs: GitHub App installation rate limits
Observed on v0.3.3.
Problem
The budget
Gatemaintains a single RESTResourceBudget(internal/budget/gate.go), and every rate-limit observation merges into it. App-JWT traffic (webhook-delivery APIs) and installation-token traffic (repository APIs) are different authentication contexts with separate GitHub rate-limit pools (App JWT ~5,000/hour for the app; GHEC installation tokens 15,000/hour), but theirx-ratelimit-*headers currently overwrite each other's shared admission state.During the 2026-08-04 rate-limit incident, ghsync reported remaining budget and kept admission open while GitHub returned "rate limit exceeded for installation ID" — consistent with a JWT response's headers masking the exhausted installation pool.
Proposed behavior
ResourceBudgetstate by credential context (installation REST, App-JWT REST; GraphQL is already separate).ghsync_c_b3_*) gain an auth-context label so exhaustion is attributable.Acceptance criteria
Refs: GitHub App installation rate limits
Observed on v0.3.3.