Files
2026-09-16 09:42:22 +01:00

6.5 KiB

created, status, requirements, system_design, legacy_specs
created status requirements system_design legacy_specs
2026-09-15 done
REQ-CI-PR-DOCS-002
REQ-CI-PR-DOCS-003
REQ-CI-PR-DOCS-004
REQ-CI-PR-SIZE-001
../../specs/ci/system-design/pull-request-documentation-coverage.md
../../specs/ci/system-design/pull-request-size-labels.md

Implementation plan: GitHub API resilience

Overview

Reduce avoidable GitHub API traffic and recover correctly when GitHub applies a transient or secondary rate limit. Two documentation-coverage runs failed with HTTP 429 while evaluating otherwise valid pull requests. Read-only replay showed about 14 requests for the smaller example and 33 for the larger example, including status publication. Per-requirement code searches and repeated pull request reads account for much of the avoidable traffic.

The CI system owns this package because it owns the trusted workflow event, request, mutation, and failure contracts.

Scope

In scope: rate-aware retries and diagnostics in the custom documentation evaluator. The package also covers request reuse, event gates, and lazy size-label definition creation.

Out of scope: replacing GITHUB_TOKEN, changing documentation policy, or changing size rules. The package does not change merge-queue membership or repository rulesets. It does not include low-frequency workflow cleanup.

Technical approach

Extend the documentation evaluator's existing bounded client instead of adding a second request library. Classify errors before each retry. Follow GitHub wait headers. Use the documented one-minute minimum for a secondary limit without a usable header. Stop within a fixed total wait budget. Emit sanitized diagnostics to the runner log.

Pass the wrapper's initial pull request snapshot into the evaluator. Build a per-snapshot requirement cache from changed exact-head documents and their base versions before using code search. Keep code search as the bounded fallback for new, moved, unresolved, and ambiguous identities.

Narrow documentation coverage to events that can change its inputs. Keep labeled and unlabeled triggers for GitHub routing, use a job-level gate for the exact no-docs-allow label, and admit edited only for base-branch retargets. Exclude title and description edits and ready-for-review changes.

For size labels, list current pull request labels once and optimistically apply the target. Create only the target definition after a narrowly recognized missing-label response. Existing target labels require no repository label-definition requests.

Open PR #3693 is implementation input for Task 01. Reuse its bounded client and diagnostic tests after rebasing. Replace its 250/500 ms no-header fallback with the documented 60/120 second waits and total budget.

Tests

Criteria Evidence
AC-CI-PR-DOCS-003.6 through .7 Client fixtures for transport, 408, 429, rate-limit 403, 5xx, headers, no-header 60/120 second waits, exhaustion, permanent errors, wait-budget overflow, and sanitized logs.
AC-CI-PR-DOCS-004.1 through .3 Exact-head/base fixtures count calls, reuse the initial snapshot, skip search for verified changed requirements, and retain new-ID, missing-ID, and duplicate-ID failures.
AC-CI-PR-DOCS-004.4 Workflow contract verifies the minimal event list and exact override-label job gate.
AC-CI-PR-SIZE-001.5, .6, .9, .10 Workflow contract verifies one current-label read, direct target apply, lazy target creation, concurrent creation recovery, and no definition preflight on the steady path.

Use fixtures from the two failed run shapes. One fixture has four requirement IDs in one changed document. The other has eleven IDs in several changed documents. Assert request classes and cache keys. Do not use live pull request numbers or mutable GitHub state.

End-to-end evidence

No application UI changes. Local evidence comes from mocked GitHub responses and workflow contract tests. After deployment, manually dispatch coverage for a covered pull request and an uncovered pull request. Exercise the override label once. Record retry diagnostics for any real throttle. Compare request counts with the pre-change 14- and 33-request replays. Do not intentionally cause live rate limiting.

Work orders

Execute sequentially. No subagents are authorized.

Verification

Run the exact commands in each work order. After all tasks:

node --test .github/scripts/pr-docs.test.cjs
python3 .github/scripts/pr-docs-workflow-contract_test.py
python3 .github/scripts/pr-size-label-workflow-contract_test.py
python3 .github/scripts/lint-action-pinning_test.py
python3 .github/scripts/lint-action-pinning.py
python3 scripts/list-docs.py validate
python3 scripts/lint-spec-files.test.py
python3 scripts/lint-spec-files.py --all
zizmor .github/workflows/pr-docs.yml
zizmor .github/workflows/pr-size-label.yml
git diff --check

Risks

  • Retrying a status write after an uncertain response can create a duplicate status record with the same context and state.
  • A conservative secondary-limit delay consumes runner time. The 180-second sleep budget keeps the workflow inside its ten-minute timeout.
  • Exact-head requirement optimization relies on the trusted base catalog's unique-ID validation and must scan every changed requirement document in the referenced directory.
  • Missing-label errors must be identified narrowly so validation and permission failures do not create labels.
  • Label-event gating must preserve merge-group and manual-dispatch paths.

References