Report what every CI job spends of its retry budget (GH-3787) - #3788
Merged
Conversation
CIAzureServiceBus was green for four consecutive main runs while burning 22 of its 25-retry budget on every one of them -- 85% of all flakiness in the repository, and invisible. Nothing distinguishes a job at 22/25 from one at 0/25. Bobcat logged all of it, and Nuke's GitHub Actions sink does turn those warnings into real annotations, so the panel was not empty. It was worse than empty: GitHub caps warning annotations at ten per check run, first-wins. On the job that spent 22 retries, 66 [FLAKY] lines in the log became exactly 10 annotations -- the same 10 you would see at 10 retries, or at 50. The count is the precise quantity GitHub throws away, and the summary line that carries it sits in the log of a job that PASSED. Three outputs, none of which gate the build: - $GITHUB_STEP_SUMMARY gets a per-project retry table, so the numbers are on the run page without opening a log and without competing for the ten annotation slots. - One ::warning carrying the COUNT, emitted before the per-test [FLAKY] warnings so it is inside the ten that survive. - A JSON ledger per job, uploaded and aggregated by a new roll-up job that diffs the run against the last main run that published one. The third is the one that matters. The absolute 22 was never the signal; the step from 1 to 22 between two adjacent runs was, and no baseline existed anywhere to notice it against. The roll-up also names jobs that reported NO ledger -- a job killed by the 20-minute cap prints nothing at all, not even a partial count, and that silence would otherwise read as clean. Deliberately not a gate. An earlier suggestion of "fail past 5 retries" is too loose: a suite legitimately at 3 would be one bad day from a red main. A nonzero count is something a human explains, which is a review norm rather than a cliff. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0116vfBcKwcjWn8msM4ZjkuA
CIAotSmoke builds and runs two AOT smoke apps and invokes no test project at all, so it has no retry budget to spend and never writes a ledger. The roll-up was reporting it as "reported no ledger" on every run -- a section that cries wolf every time is exactly how a signal channel gets ignored, which is the failure this report exists to undo. Also: `paste -sd', '` cycles the two delimiters rather than treating them as one separator, so the missing-jobs list rendered as "a,b c,d". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0116vfBcKwcjWn8msM4ZjkuA
This was referenced Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3787.
CIAzureServiceBusreported green for four consecutivemainruns while burning 22 of its25-retry budget on every one of them. That was 85% of all flakiness in the repository, and nothing
in the GitHub UI distinguishes a job at 22/25 retries from one at 0/25.
The annotations were not the safety net they looked like
Bobcat logs every retry, and Nuke's GitHub Actions sink does turn those Serilog warnings into real
::warningannotations — so the Annotations panel was never empty. It is worse than empty: GitHubcaps warning annotations at ten per check run, first-wins. On run 30759855844, the ASB job that
spent 22 retries:
Ten
[FLAKY]lines. You would see ten at 10 retries too, and ten at 50. The count is the exactquantity GitHub discards, and the one line that does carry it (
239 passed (0 retries, ...)) livesin the log of a job that passed, which nobody has a reason to open.
What this adds
$GITHUB_STEP_SUMMARY::warningwith the count[FLAKY]warnings, so it is inside the ten that surviveaggregate.jsonper runThe third is the one that matters. The absolute 22 was never the signal — the step from 1 to 22
between two adjacent runs was, and no baseline existed anywhere to notice it against. The new
Flakiness roll-upjob aggregates all thirty jobs and reports the delta:It also names any job that reported no ledger at all. Bobcat prints its summary only at the end,
so a job killed by the 20-minute cap reports nothing — not even a partial count — and that silence
would otherwise read as clean.
Deliberately not a gate
An earlier suggestion of "fail past a threshold of 5 retries" is too loose: a suite legitimately
sitting at 3 today would be one bad day from a red
main. A nonzero count is something a humanexplains — a review norm, not a cliff. Nothing here can fail a build;
flakiness-report.shexits 0unconditionally.
Verification
CISqlitelocally underGITHUB_ACTIONS=truewith two tests rigged (marker file) to fail onfirst attempt and pass in the retry process. All three outputs produced the real numbers:
step-summary table with
**2**bolded, the counted::warningahead of the[FLAKY]lines, andthe JSON ledger naming both tests.
flakiness-report.shagainst synthesized ledgers for the no-baseline, with-baseline,all-clean, and empty-input cases. A
FLAKINESS_BASELINE_FILEescape hatch exists for exactly this.The real test is this PR's own
Flakiness roll-upjob — it is the first run, so it will report"no baseline found" and absolute numbers only.
🤖 Generated with Claude Code
https://claude.ai/code/session_0116vfBcKwcjWn8msM4ZjkuA