Files
kandev/docs/plans/github-api-resilience/plan.md
T
2026-09-16 09:42:22 +01:00

138 lines
6.5 KiB
Markdown

---
created: 2026-09-15
status: done
requirements:
- REQ-CI-PR-DOCS-002
- REQ-CI-PR-DOCS-003
- REQ-CI-PR-DOCS-004
- REQ-CI-PR-SIZE-001
system_design:
- ../../specs/ci/system-design/pull-request-documentation-coverage.md
- ../../specs/ci/system-design/pull-request-size-labels.md
legacy_specs: []
---
# 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
- [x] [Task 01: Add rate-aware API recovery](task-01-rate-aware-api-recovery.md)
- [x] [Task 02: Consolidate documentation API reads](task-02-consolidate-documentation-api-reads.md)
- [x] [Task 03: Remove size-label definition preflights](task-03-remove-size-label-definition-preflights.md)
Execute sequentially. No subagents are authorized.
## Verification
Run the exact commands in each work order. After all tasks:
```bash
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
- [Documentation coverage requirements](../../specs/ci/requirements/pull-request-documentation-coverage.md)
- [Documentation coverage design](../../specs/ci/system-design/pull-request-documentation-coverage.md)
- [Pull request size requirements](../../specs/ci/requirements/pull-request-size-labels.md)
- [Pull request size design](../../specs/ci/system-design/pull-request-size-labels.md)
- [Documentation coverage decision](../../decisions/2026-09-10-pr-documentation-coverage.md)
- [GitHub REST API rate limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api)
- [GitHub REST API best practices](https://docs.github.com/rest/guides/best-practices-for-integrators)
- [Open retry implementation PR #3693](https://github.com/kdlbs/kandev/pull/3693)