Skip to content

[cross-repo] Parallel workers share one GraphQL budget; exhausting it makes claim's Status write fail silently (causes the CLAIM-STATUS-LAG drift check-board reconciles) #418

Description

@EHotwagner

From: FS.GG.Rendering, found while working FS-GG/FS.GG.Rendering#413 (worker heron-4b7).
Contract: the intra-repo-parallel-work / cross-repo-coordination protocol (ADR-0021, ADR-0027) and scripts/fsgg-coord.

The defect

N parallel workers authenticate as one GitHub account, so they also share one 5,000/hr GraphQL budget — and fsgg-coord's read commands (ready, batch, next, who, lint) are full-board GraphQL scans. With 4–5 workers looping /pnext-item, that budget is exhausted in minutes.

Observed this session, in a single repo, inside ~15 minutes:

12:31  graphql remaining 4136/5000
12:43  graphql remaining    0/5000   <- exhausted

Everything that writes then fails, mid-protocol:

Why this is worse than "slow down"

Rate-limit exhaustion silently causes the board drift that /check-board exists to reconcile.

claim sets Status: In progress in a subshell that is best-effort and swallows the failure. A swallowed 5xx and a swallowed 403-rate-limit are indistinguishable to it. So under exhaustion the lock is taken but the board is never updated — which is exactly CLAIM-STATUS-LAG.

This is not hypothetical: /check-board this session found #429 sitting at Status: Blocked while shrike-a91 actively held it with PR #433 open. next/take skip a Blocked item, so that item was invisible to every other worker for as long as the drift stood.

The protocol's own failure mode is therefore load-dependent: the more workers you fan out, the more the board lies — and fan-out is the entire point of the protocol.

What's missing

fsgg-coord budget exists, so the cap is known — but nothing acts on it:

  1. claim must not swallow a rate-limit failure. A 403/429 on the Status write is not a transient 5xx; it is a known, predictable condition. Fail loudly, or retry after resetAt, or record the pending write for the next command to flush.
  2. Reads should not cost a full-board GraphQL scan per worker per loop. take/next re-scan the whole 630-item board every call. A short-TTL cache, or an ETag/conditional read, or a REST path for the hot reads would cut this by most of its cost.
  3. Prefer REST where REST exists. PR creation, issue comments, and issue reads are all available over REST, which has an independent 5,000/hr budget that sat at ~4,700 while GraphQL was at 0. Today gh silently prefers GraphQL and the protocol inherits that.
  4. Degrade, don't die. take should tell a worker "GraphQL exhausted, resets in Nm" rather than surfacing a raw gh error — or better, back off and retry.

Suggested owner

FS-GG/.github owns the canonical protocol and scripts/fsgg-coord. Filing here rather than fixing in Rendering: the script and the ADRs are not ours to widen into.

Cross-ref: FS-GG/FS.GG.Rendering#413, PR FS-GG/FS.GG.Rendering#449.

Paths: scripts/fsgg-coord tests/fsgg-coord/ docs/coordination/graphql-budget.md .claude/skills/cross-repo-coordination/ .agents/skills/cross-repo-coordination/ .claude/skills/pnext-item/ .agents/skills/pnext-item/

Metadata

Metadata

Assignees

Labels

cross-repoTouches more than one FS-GG repocross-repo:requestIncoming request from another repo

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions