Skip to content

[v1.0] Make Project Intake resilient to GraphQL quota exhaustion #311

Description

@codeforester

Goal

Keep issue-to-Project reconciliation reliable when the authenticated user's shared GraphQL quota is exhausted.

Background

Hosted Project Intake run 33497612878 failed while reconciling issue #302:
https://github.com/basefoundry/base-cli/actions/runs/33497612878

GitHub API pressure during Project Intake: view issue
Waiting 60s and retrying once.
GraphQL: API rate limit already exceeded for user ID 22363102.

The first operation uses gh issue view --json, which consumes GraphQL even though issue state and URL are available from REST. The workflow then performs separate project list, view, item-add, item-list, field-list, and five item-edit calls:

issue_json="$(project_intake_gh "view issue" gh issue view "$issue_number" --repo "$GITHUB_REPOSITORY" --json state,url)"
issue_state="$(jq -r '.state' <<<"$issue_json")"
issue_url="$(jq -r '.url' <<<"$issue_json")"
project_number="$(
project_intake_gh "list Projects" gh project list --owner "$BASE_PROJECT_OWNER" --format json --limit 100 |
jq -r --arg title "$BASE_PROJECT_TITLE" \
'.projects[] | select(.title == $title) | .number' |
head -n 1
)"
if [[ -z "$project_number" ]]; then
echo "::error::GitHub Project '$BASE_PROJECT_TITLE' was not found for owner '$BASE_PROJECT_OWNER'."
echo "::error::If this Project exists, set BASE_PROJECT_TOKEN with user Project read/write access."
echo "::error::Fix: gh auth token | gh secret set BASE_PROJECT_TOKEN --repo $GITHUB_REPOSITORY"
exit 1
fi
project_id="$(project_intake_gh "view Project" gh project view "$project_number" --owner "$BASE_PROJECT_OWNER" --format json --jq '.id')"
item_id="$(project_intake_gh "add Project item" gh project item-add "$project_number" --owner "$BASE_PROJECT_OWNER" --url "$issue_url" --format json --jq '.id')"
item_json="$(
project_intake_gh "list Project items" gh project item-list "$project_number" --owner "$BASE_PROJECT_OWNER" --format json --limit 1000 |
jq --arg id "$item_id" '.items[] | select(.id == $id)'
)"
fields_json="$(project_intake_gh "list Project fields" gh project field-list "$project_number" --owner "$BASE_PROJECT_OWNER" --format json)"

project_intake_gh "set Project field $field_name" gh project item-edit \
--id "$item_id" \
--project-id "$project_id" \
--field-id "$field_id" \
--single-select-option-id "$option_id" \
>/dev/null
}
set_single_select_if_missing() {
local field_name="$1"
local item_key="$2"
local option_name="$3"
local current_value
current_value="$(jq -r --arg key "$item_key" '.[$key] // ""' <<<"$item_json")"
if [[ -n "$current_value" ]]; then
return 0
fi
set_single_select "$field_name" "$option_name"
}
status_value="$BASE_PROJECT_DEFAULT_OPEN_STATUS"
if [[ "$issue_state" == "CLOSED" ]]; then
status_value="$BASE_PROJECT_DEFAULT_CLOSED_STATUS"
fi
set_single_select Status "$status_value"
set_single_select_if_missing Priority priority "$BASE_PROJECT_DEFAULT_PRIORITY"
set_single_select_if_missing Size size "$BASE_PROJECT_DEFAULT_SIZE"
set_single_select_if_missing Area area "$BASE_PROJECT_DEFAULT_AREA"
set_single_select_if_missing Initiative initiative "$BASE_PROJECT_DEFAULT_INITIATIVE"

When the CLI error lacks rate-limit headers, retry delay falls back to 60 seconds even if the quota resets much later. The retry then fails and newly opened/closed issues can remain absent or stale in the Project.

Scope

  • Read issue state/URL through REST.
  • Minimize GraphQL round trips and cache stable Project/field/option identifiers safely.
  • Batch mutations or use one bounded reconciliation query/mutation sequence where practical.
  • Honor actual reset time and workflow timeout, including resumable/re-dispatch behavior.
  • Make reconciliation idempotent after partial writes.

Acceptance criteria

  • Exhausted GraphQL quota before an issue event does not lose the requested reconciliation.
  • REST-readable issue metadata does not consume GraphQL.
  • A quota reset beyond 60 seconds is handled from rate-limit data or deferred/retried without a false permanent failure.
  • Re-running the workflow after any partial step produces exactly one Project item with correct fields.
  • Tests cover primary-limit, secondary-limit, authentication, timeout, and partial-mutation cases.
  • Operational logs report calls/points used without exposing the token.

Validation

Use mocked quota responses plus a low-quota integration rehearsal and verify Project fields by independent readback.

Project fields

  • Status: Backlog
  • Priority: P1
  • Area: CI
  • Initiative: v1.0 Readiness
  • Size: M

Ownership

Release review follow-up — 2026-10-01

Reopening after current-main issue intake failed for all five review issues #407-#411. Reviewed revision: 7d8e7d7e902ace88495c5e0ff3b9db4f4a7bf5d2.

Example failure: https://github.com/basefoundry/base-cli/actions/runs/36823371513/job/110243571194

GitHub API command failed during Project Intake: list Projects
unknown owner type
Process completed with exit code 1.

The other four intake runs failed as well: 36823370481, 36823366627, 36823367417, and 36823363910. Issue reads succeeded. The workflow still uses gh project list/view/item-add/item-list/field-list/item-edit for the Project path. It neither has a REST Project transport nor verifies all final fields by independent readback.

The review session independently observed exhausted shared GraphQL quota at the same time; the local Base Project wrapper recovered via REST. The hosted message masks its underlying owner-lookup failure, so confirm the causal error rather than assuming token expiry or an invalid organization. Do not rotate a valid token merely because this message appears.

Review issues #407-#411 were manually reconciled using the supported Base wrapper/REST fallback, and all five fields plus milestones/assignment were read back successfully. That repairs these cards but does not close the workflow reliability defect. The original acceptance criteria remain unmet. The sibling base-bash-libs REST intake is a current reference implementation.

Follow-up acceptance: test unavailable GraphQL/owner resolution, preserve active issue status and existing fields during recovery, make partial writes idempotent, and verify every managed field before reporting success. Complete the original quota/timeout/authentication tests as well. No credentials should be included in fixtures or logs.

Release-review metadata: Ready / P1 / CI / v1.0 Readiness / M; milestone v1.0.0; assignee codeforester.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

ciContinuous integration, tests, automation, or release workflows

Type

No type

Projects

  • Status
    Ready

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions