Repository navigation
CI shepherd: ci_digest (triage + error lines + local repro) and ci_shepherd (merge / rerun / one note per head) (Part of #4790) - #4819
Merged
Conversation
… pr_check --digest (Part of #4790) Step 1 of the CI shepherd. Nobody -- person or model -- should read a raw CI log or wait on CI to learn whether a fix works. `ci_digest.py N` (and `pr_check.py N --digest`) gives, per failed check on the PR head: - triage: dirty (action_required on a conflicting PR: merge main, regenerate generated files), infra (cancelled, startup failure, lost runner, disk, rate limit: rerun once), flake (only #4477's wall-clock tests failed: rerun once), real (a fixer's job); - the job log's error lines and last 40 lines, ANSI codes and timestamps stripped; - the local repro: pr_gates' own gate / ratchet names where one exists, the failing pytest node ids, the det cases not proven; checks with no local equivalent say so. Tried live on #4818: its one "failure" was action_required on a dirty PR, now triaged dirty, not real. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| from typing import Any | ||
|
|
||
| sys.path.insert(0, str(Path(__file__).resolve().parent)) | ||
| import pr_check # noqa: E402 |
| res = check(args.number, api) | ||
| print(json.dumps(res, indent=1) if args.json else report(res)) | ||
| if args.digest and res["checks"]["failed"]: | ||
| import ci_digest # noqa: PLC0415 -- fetches job logs; only when asked |
…flakes once, one note per head (Part of #4790) Step 2 of #4790, replacing handoffs/tools/merge_watch.sh + merge_train.sh. Per queued PR each pass: merged/closed leaves; --after waits for its base; green + mergeable is merged (pr_check.merge, pinned to the head, crucible pin synced); pending waits; infra/flake failures are rerun ONCE per (head SHA, check); a dirty PR gets one note; a real failure gets one comment with ci_digest's digest (error lines, log tail, local repro), and $CI_SHEPHERD_FIXER, if set, is started with the PR and the digest file. A new push resets the once-per-head memory. One shepherd at a time via flock on a run lock (never a pgrep that matches itself); add/drop edit the queue between passes under a state lock. Census history stays out (it writes a committed file: the evidence bot's job). ci_digest entries now carry run_id / job_id. Tests: fake gh, 7 cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
squid-protocol
marked this pull request as ready for review
October 9, 2026 20:53
This was referenced Oct 9, 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.
Steps 1–2 of #4790. Nobody (person or model) should read a raw CI log, wait on CI, or re-push to find out whether a fix works.
1.
tests/tools/ci_digest.py N(alsopr_check.py N --digest). Per failed check on the PR head:dirty(action_required on a conflicting PR),infra(cancelled, startup failure, lost runner, disk, rate limit),flake(only Flaky: wall-clock-bound tests fail under CI load (regex_redos, many_move_statements_stay_linear) #4477's wall-clock tests failed) orreal;--jsonfor tools.2.
tests/tools/box/ci_shepherd.pyreplaces handoffs/tools/merge_watch.sh + merge_train.sh. It keeps one queue, run by one process (an flock run lock, so it never matches itself inpgreplike the old watchers did). Each pass:--afterwaits for a stacked PR's base;gh run rerun --failedonce per (head, check);$CI_SHEPHERD_FIXER N digest.jsonif set.A new push resets the once-per-head memory. Census history stays out (it writes a committed file, so it is the evidence bot's job).
Live: the digest on #4818 triaged its
action_requiredasdirty(it wasrealbefore the fix). The shepherd is running for the current queue (#4811 → #4780/#4810/#4818, #4819) from this branch.Tests: test_ci_digest (6) + test_ci_shepherd (7, fake gh) + test_pr_check: 21 passed. Ruff and mypy audits clean;
pr_gates --fast5/5. No ledger, evidence or golden files touched.Next (#4790 step 3): a fixer hook that runs agy or Claude on the digest's repro; auto re-merge when only generated files conflict.
🤖 Generated with Claude Code