Repository navigation
perf(ci): count every pr-merge job's GITHUB_TOKEN calls - #10601
Conversation
dispatch-full-run, unarmed-status and gate-check called GitHub with the job token and wrote no api-calls notice. Each now ends with the log-api-budget step; dispatch-full-run gets a sparse checkout of the library (contents: read) and records its cover read and each dispatch POST, record-only. The dispatch POST in post-merge-dispatch is recorded the same way. api-budget.bash's -i branch now keeps the reading of a call the caller sends with -i, so it is counted (pr-require-checks.yml's verbatim copy too). enable-auto-merge logs the verifier reader's babysit-gh-calls line per pull request, as the merge job's tee does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0144XNKz9sKFKYo4gQtVW78R
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
claude-security-review reviewed Security review of #10601 at a780943: no security findings. critical-findings: 0 I read the whole diff and the PR context. The change touches
No manifest files changed, so no |
|
claude-review reviewed Review of #10601: count every pr-merge job's GITHUB_TOKEN callsNo findings. I read the full diff and the surrounding code. I did not run any tests, so the PR's test results are author-claimed and unverified. What I checked
Trusted paths. The change touches Spend. The change adds one sparse checkout per dispatch-full-run run and a few log steps. It uses Actions runner time and no paid setting or billed resource, so there is no hold. Closing issues. The PR states "No related issue", so there are no acceptance criteria to check. |
No related issue: 52% of GITHUB_TOKEN spend unmeasured (GitHub Support, 00:49-01:42Z)
Summary
GitHub Support counted 4,069 GITHUB_TOKEN calls between 00:49 and 01:42Z on 2026-10-28, and only 1,941 of them came with an
api-callsnotice. 17 of the 22 pr-merge runs in that hour emitted none. Three pr-merge jobs call the API with the job token and log nothing: dispatch-full-run (the only job a push to main runs), unarmed-status and gate-check. This PR makes all three log their calls the way the other pr-merge jobs already do. It is instrumentation only.Fix
.baseand call GitHub throughcreateGitHub, which records a reading for each response underAPI_BUDGET_TOKEN. Each job now setsAPI_BUDGET_TOKEN: github-tokenand ends with thelog-api-budgetstep (if: always(),continue-on-error). This is the same change ci: log GITHUB_TOKEN api-calls from ci-status, post-merge-dispatch and intake-reenter #10551 made to post-merge-dispatch..github/actions/log-api-budget/only, as pr-feedback-dispatch's rerun-ci-status job does. The checkout hascontinue-on-error, so if it fails, the job calls GitHub exactly as before and logs nothing. Its token gainscontents: read, which the checkout needs. The cover read sourcesapi-budget.bashand wraps gh, as post-merge-dispatch's copy of the same step already does.gh api -ioutput is passed toapi_budget_read. The POST is recorded, never guarded, so a stop recorded earlier in the job cannot refuse a dispatch that goes out today. The two step bodies stay identical.-icounting gap. Inapi-budget.bash, the-i|--includebranch of the gh wrapper observed the headers but never kept the reading, so a wrapped-icall went uncounted. It now callsapi_budget_read "$API_BUDGET_GH_TOKEN" "$out" >/dev/nullbeforecat "$out". pr-require-checks.yml's verbatim copy inAPI_BUDGET_SHgets the same line. No wrapped caller also reads-ioutput itself, so nothing is counted twice.pythonVerifyread only the totals from the reader'sbabysit-gh-callsline, so the per-endpoint JSON never reached the log. It now prints that line once per pull request, re-serialized from the parsed JSON so it is always one line. The shape matches the line the merge job tees, so one grep sums the calls per endpoint across both jobs.not-modified=Mfrom an EXIT trap. That poll no longer exists: perf(pr-merge): batch main's whole-suite runs and stop sleeping after the dispatch #10491 removed it, and it merged at 00:36Z, before the window. No conditional GET is left in pr-merge.yml. The job-end log step runsif: always()and already emitsnot-modified=M, and the new test below shows a wrapped 304 is counted there.Cost. The only new checkout is one sparse checkout per dispatch-full-run run. The same checkout in rerun-ci-status took 2-3 s on recent runs. unarmed-status and gate-check add no checkout, only the log step, which makes no API call.
Still not counted. The checkout's own git fetch and the artifact actions. Also github-token calls in merge and enable-auto-merge runs that never mint a lane token: their log steps run only when
steps.lane-token.outputs.token != '', and a test pins that condition.This PR touches
pr-merge.ymlandpr-require-checks.yml, which are on the merge-holds path list, so a person lifts the hold before it merges.Verification
every job that calls GitHub with the job token logs its api-calls(workflow.test.mjs). It finds every pr-merge job that uses${{ github.token }}, without a hard-coded list. On main it failed with[ 'unarmed-status', 'dispatch-full-run', 'gate-check' ]. It passes on this branch.a -i call is counted in api-calls, and a 304 as not-modified(log-api-budget.test.mjs). Before the fix the output was''. After the fix it printsgithub-token=1 not-modified=1.github-token=2). ThepythonVerifytest now asserts the calls line is logged.node --test .github/scripts/pr-merge/*.test.mjs .github/actions/log-api-budget/*.test.mjs .github/actions/check-trusted-trigger/*.test.mjs: 1054 pass, 0 fail.scripts/ci-rerun-ci-status.test.shandscripts/ci-lanes-pr-hold.test.shpass.test_merge_holds_sync.py: 10 passed. shellcheck finds nothing inapi-budget.bash. actionlint reports only the existingconcurrency.queuefindings.runner-policy.mjs, which could not run locally (itsajvdependency is not installed in the worktree); CI runs it.Related
🤖 Generated with Claude Code