Repository navigation
[local-fix-worker-throttle-ci-watcher-credentia] fix(worker): throttle CI-watcher credential failures and make them actionable - #372
Conversation
…worker): throttle CI-watcher credential failures and make them actionable
- Branch GitHub credential guidance by failure class (401/invalid vs 403/missing permission) and drop the contradictory 'restart the worker' advice; share the helper between CI and review pollers. - Key the CI watcher's credential warning throttle by repo so one repo's 403 cannot hide another repo's 401 in fleet mode, with per-repo recovery. - Update review polling to use the shared guidance and document that credential failures intentionally skip captureError. - Add tests for distinct per-repo failures, mixed success/failure ticks, and 403s originating from the Actions stream; assert review-poller 403 guidance. - Update docs/code/worker.md to describe the shared throttle, guidance, and per-repo isolation. - Iteration 2: persist escalatedSha only after the escalation comment lands, retry failed posts, and route their credential failures through the per-repo throttle/recovery path. - Iteration 2: throttle review-polling credential warnings and 404-access notices per repo with a recovery line, and never treat a 404 as credential recovery. - Iteration 2: classify only known credential-denial 403 bodies as auth failures so archived/locked/blocked/unknown 403s keep the normal capture/retry path; document the distinction. - Iteration 2: add tests for escalation-post retry paths, per-repo review throttling, and non-credential 403 handling. - Iteration 3: name the write permission (Issues/Pull requests: Write) for escalation-comment 403s and throttle CI read vs comment-post warnings independently, so a CI-read warning cannot suppress a write failure. - Iteration 3: cap non-credential escalation-comment retries per head SHA and stop retrying after the cap instead of POSTing and warning on every poll. - Iteration 3: track review-polling and CI recovery per failing endpoint stage so metadata-only success cannot clear a throttle that was never re-tested; add regression tests.
Artifacts & verificationRun: 45 · Commit: Change explanationProduced by Throttle CI-watcher credential failures and make them actionableNo PR template exists in this repository, so no template sections were required. Why this change is neededPer the task description: the worker's CI failure watcher polls CI for every open agent PR on each interval. When GitHub rejects the credential or permission, The task also notes existing precedent: I did not independently verify the host log counts or the token's permissions; that motivation is taken from the task. What changedCredential-failure classification. CI watcher throttling and recovery. Escalation-comment handling (beyond the literal task). Escalation state ( Review polling (beyond the literal task). The single instance-wide auth-warning throttle is replaced with per-repo, per-stage state ( Docs and tests. Validation actually performedRecorded in the pipeline artifacts, not re-run for this write-up:
No automated validation was performed as part of writing this explanation. Material risks
Where reviewers should focus
Observed evidence vs inferenceObserved from the diff and repository: the code paths, patterns, tests, and documentation described above; the repository has no PR template; the Taken from pipeline artifacts, not independently confirmed: the test counts, typecheck result, and lint result. Inferred, not observed: the risks about restart behavior, crash-window duplication, and delayed recovery are reasoning from the implementation, not reproduced or tested in the provided evidence. Visual explanationProduced by Show artifactSummaryThe CI failure watcher used to log one identical warning per PR per tick whenever GitHub rejected its credential, so a token missing CI read scopes flooded the worker log indefinitely without telling the operator what to fix. This change classifies credential-scoped 403 responses as credential failures, throttles those warnings to once per repo per operation per hour with the exact missing permissions named, and logs a single recovery line when the same endpoint succeeds again. Transient failures (404, 5xx, rate limits, non-credential 403s) keep their existing per-tick reporting. It matters because CI auto-fix currently never runs for those PRs while the operator only sees noise. EvidenceRouting of one failed PR poll, as implemented: Scope of the new classification, gathered from the merged diff:
Recovery is gated on the endpoint actually being re-read: the CI watcher only claims recovery when Validation, taken from the pipeline artifacts and not re-run while writing this:
Merge DangerBlast radius is narrow but not zero. Verification reportProduced by Show artifactFurther artifacts omitted because they exceed the comment preview budget. |
Why
Task local-fix-worker-throttle-ci-watcher-credentia: fix(worker): throttle CI-watcher credential failures and make them actionable
CI polling treated a permanent GitHub credential/permission failure as a per-PR, per-tick error. Two inaccessible PRs produced ~1,197 identical warnings because the watcher retried the same rejected token forever. The shared auth classifier also missed GitHub’s 403 permission-denial response, so CI and review polling could not recognize it as a non-transient credential problem.
What changed
isGitHubAuthErrorinpackages/code/src/lib/code-host/github/reviews.tsto treat non-rate-limit403responses as credential failures, includingResource not accessible by personal access token.404,5xx, and403rate-limit/abuse-detection responses remain non-auth errors. JSDoc now documents the 403 case and exclusions.packages/code/src/lib/acquirers/ci-failure-watcher.ts:nowclock.[poll:ci-failures]and the failingrepo#number, includes the original error, namesActions: ReadandCommit statuses: Read, says CI fixes resume once reads succeed and polling continues, and points todocs/code/worker.md.docs/code/worker.md.Scope
Included: the shared auth classifier, CI watcher logging/throttle/recovery, relevant docs, and unit tests. Excluded: polling cadence, watch/unwatch behavior, green-PR backoff, retry budgets, escalation, fix invocation, dashboard/UI work, Sentry, Jira, mentions, other acquirers, new dependencies, and CLI surface changes. No commit, push, label, or PR was created.
Tradeoffs
The classifier treats any non-rate-limit 403 as a credential/permission failure rather than matching only specific message phrases. This avoids missing less common GitHub permission wording, at the cost of also throttling other permanent 403s.
ReviewPollingAcquirernow throttles non-rate-limit 403s too; a test pins that behavior, which is consistent with the task’s desired outcome.Blast Radius
isGitHubAuthErrorhas only two consumers:CiFailureWatcherAcquirerandReviewPollingAcquirer. The CI watcher now suppresses repeated credential warnings and logs recovery; review polling now also treats permission-denial 403s as auth failures and throttles them like 401s. Existing 404, 5xx, and rate-limit handling is unchanged.Verification
bun run packages/code/tests/run-tests.ts ci-failure-watcher-acquirer.test.ts github-reviews-client.test.ts review-polling-acquirer.test.ts→ 85 pass, 0 fail (256 assertions).bun run --filter @getdevintern/code typecheck→ exit 0.bun run --filter @getdevintern/code format:check→ all files formatted correctly.bun run --filter @getdevintern/code lint→ exit 0.bun run lint→ Turbo lint passed for all 12 packages, but the final rootoxlint examples/pipeline-pluginsstep was blocked by the nono sandbox while resolving/agent-harness(Permission denied). This is an environment/sandbox limitation unrelated to the changed files; package-level lint passed.This PR was automatically created by @devintern/code