fix(ci): the Gate's own status post could pick a token that cannot write statuses - #81
Conversation
…ite statuses A green Gate run left a RED `Gate / gate` status on PR #54, and nothing could clear it. The summary job computed `STATE: success`, then: Token registry initialized with 7 tokens Selected token: WORKFLOWS_APP (4645 remaining, 92.9% capacity) POST /repos/stranske/Orchestrator/statuses/bfb6fb1... - 403 ##[warning]Gate commit status update blocked by permissions; leaving existing status untouched. `createTokenAwareRetry` defaults to `env: process.env`, so it collected every App and PAT secret this job exposes and picked by remaining CAPACITY. `POST /statuses/{sha}` needs the `statuses` scope: `GITHUB_TOKEN` has it here (the job declares `statuses: write`, and the runner printed `Statuses: write` for both runs), the WORKFLOWS App installation does not. So the token that posts the Gate's status was chosen by rate-limit headroom, and when the App won, the post 403'd and `isIntegrationPermissionError` swallowed it as best-effort. That is a latched gate. Maint 71 will not merge a sync PR without `Gate / gate = success`; the only writer of that status is this step; and the step was refused the write — so the stale failure outlived its evidence and waiting could not fix it. It failed toward SILENCE: a warning inside a run whose 18 jobs were all green. Non-deterministic by construction, which is why it looked intermittent: run 32659615306 posted fine at 18:56 and 32661355234 was refused at 19:28/19:34 with identical declared permissions, because the balancer picked differently. Other PRs' statuses posted normally all day. `env: {}` pins this one call to the workflow token. Retries still apply; only the token source is fixed. Declaring `capabilities: ['statuses:write']` would NOT have worked: token_load_balancer aliases that to the generic `write-repo`, which all three token types claim, so the App stays eligible. That alias is the upstream half of this defect and is untouched here. Also: the mypy figure was stale in six places and disagreed with itself. Measured on 24cb115 with `python3 scripts/ci_lint_baseline.py` (pinned mypy 2.3.1): 608 errors across 89 of 189 files, 19 codes, top 15 covering 603. Recorded was 604/90 in the toggle and both 604/89 and 601/89 in the baseline doc. It read 601 -> 604 -> 607 -> 608 during 2026-08-23 alone, and nothing couples it to a measurement, so the illustrative prose that restated it ("a bare '604 errors' reads as be-patient") is now generic — that phrasing was manufacturing the next stale figure every time the count moved. The drift itself is recorded where the number is. Verified: test_ci_gate_config.py 12 passed; ruff and black -l 100 clean over 196 files; pr-00-gate.yml parses (11 jobs); collection unchanged at 416 = the recorded floor, so no floor edit. No behaviour change outside the token source and the prose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 17 seconds. View limit detailsLimit details: You’ve used the included review currently available. Your 73 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
Comment |
Workflow source neededPR #81 needs either a linked GitHub issue or one valid non-issue Workflow Source before PR metadata automation can manage it safely. Please do one of:
Once a valid source is present, this warning will not be reposted. |
Automated Status SummaryHead SHA: 8d76a30
Coverage Overview
Coverage Trend
Top Coverage Hotspots (lowest coverage)
Low Coverage Files (<50.0%)
Updated automatically; will refresh on subsequent CI/Docker completions. Keepalive checklistScopeNo scope information available Tasks
Acceptance criteria
|
|
Workflow state fingerprint for Agents Gate Followups. Do not edit. |
What broke
A fully green Gate run left a red
Gate / gatecommit status on #54, and nothing could clear it. The summary job computed the right answer and was then refused the write:createTokenAwareRetrydefaults toenv: process.env, so it collected every App and PAT secret the job exposes and chose by remaining rate-limit capacity.POST /statuses/{sha}needs thestatusesscope —GITHUB_TOKENhas it here (the job declaresstatuses: write; the runner printedStatuses: writefor both runs), the WORKFLOWS App installation does not.Why it is a latched gate
Maint 71 will not merge a sync PR without
Gate / gate = success. The only writer of that status is this step. The step was refused the write. So the stalefailureoutlived its evidence and waiting could not clear it — and it failed toward silence: a warning buried in a run whose 18 jobs were all green.It looked intermittent because token choice is capacity-based: run
32659615306posted fine at 18:56,32661355234was refused at 19:28 and again at 19:34 on a re-run, with identical declared permissions.The fix
env: {}pins this one call to the workflow token. Retries still apply; only the token source is fixed.Declaring
capabilities: ['statuses:write']would not have worked —token_load_balanceraliases that to the genericwrite-repo, which all three token types claim, so the App stays eligible. That alias is the upstream half of the defect and is untouched here (it lives instranske/Workflows, and every consumer's Gate carries the same call).Second, unrelated correction
The mypy figure was stale in six places and disagreed with itself. Measured on
24cb115withpython3 scripts/ci_lint_baseline.pyunder the pinned mypy 2.3.1: 608 errors across 89 of 189 files, 19 codes, top 15 covering 603. Recorded was 604/90 in the toggle, and both 604/89 and 601/89 in the baseline doc.It read 601 → 604 → 607 → 608 during 2026-08-23 alone and nothing couples it to a measurement, so the illustrative prose that restated it ("a bare
604 errorsreads as be-patient") is now generic — that phrasing was manufacturing the next stale figure every time the count moved. The drift is recorded where the number lives.Verification
test_ci_gate_config.py— 12 passedruff checkandblack --check -l 100— clean over 196 filespr-00-gate.ymlparses, 11 jobsNo behaviour change outside the token source and the prose.
🤖 Generated with Claude Code