Das Problem
Pull Requests an fremde Projekte bleiben auf zwei Arten liegen. Manche warten auf den Autor (ein Merge-Konflikt, ein fehlschlagender Check, eine Frage aus dem Review), und der Autor bemerkt es nicht. Andere schließt ein Stale-Bot nach Wochen ohne Review, manchmal nachdem sie schon freigegeben waren. Die Benachrichtigungen von GitHub trennen diese Fälle nicht, und eine Schließung durch einen Bot sieht aus wie jede andere.
Über Stale-Bots wird immer wieder geklagt (Reaktionen Stand 2026-10-20):
- actions/stale#719, "Default behavior can be disruptive for contributors of a repository", offen, 17 Reaktionen.
- actions/stale#861, "Issues should be kept active if reactions or backlinks", offen, 17 Reaktionen.
- Anträge, den Stale-Bot zu entfernen, in nim-lang/Nim#15159 (30), arvidn/libtorrent#6797 (19), prusa3d/Prusa-Firmware-Buddy#5133 (10) und signalapp/Signal-iOS#5931 (9).
Dashboards wie gh-dash zeigen offene Pull Requests an. Ein Werkzeug, das von Bots geschlossene Pull Requests findet oder für jeden offenen sagt, wer handeln muss, wurde nicht gefunden.
Wie es funktioniert
pr-rescue nutzt die Anmeldung der GitHub-CLI und einige GraphQL-Suchanfragen, um die offenen, geschlossenen und gemergten Pull Requests des Nutzers in fremden Repositories abzurufen. Es teilt sie in drei Gruppen ein:
- Wartet auf dich: Merge-Konflikt, angeforderte Änderungen, fehlschlagende
Checks, Entwurf oder ein unbeantworteter Kommentar einer Person seit dem
letzten eigenen Commit. Dazu kommen Pull Requests, die ein Bot ohne Merge
geschlossen hat, markiert mit
APPROVED, wenn sie freigegeben waren, und mit dem Hinweis, ob der Branch noch existiert. Davon hängt ab, ob ein Reopen funktionieren kann. - Wartet auf die Maintainer: freigegeben, aber nicht gemergt, noch kein Review oder abgebrochene Checks, die einen Neustart brauchen, den ein Fork nicht auslösen kann, jeweils mit den Tagen ohne Aktivität.
- Bereits erledigt: von Bots geschlossene Pull Requests, auf die ein späterer eigener Pull Request verweist, etwa mit "Replaces #123".
Pull Requests, die eine Person oder der Autor selbst geschlossen hat, fehlen in
der Liste. Checks werden einzeln gelesen. Ein Status, der nur meldet, dass eine
Deployment-Vorschau freigegeben werden muss (Vercels "Authorization required to
deploy" bei Pull Requests aus Forks), zählt nicht als Fehler. Ein Gate-Job wie
enforce-all-checks, der nur fehlschlägt, weil ein anderer Job abgebrochen
wurde, wird nicht gesondert gemeldet. Fehlschlagende Checks stehen mit Namen in
der Ausgabe.
Das ist die Beispielausgabe aus der README mit erfundenen Repository-Namen:
$ pr-rescue
Waiting on you (3)
acme/router#5059 checks failing: test (3.11) [open, 78d idle]
Add retry budget to the proxy
https://github.com/acme/router/pull/5059
acme/sdk#1472 closed by github-actions, branch still exists: reopen or resubmit [closed, 68d idle]
fix(cli): pass keyword arguments to the start function
https://github.com/acme/sdk/pull/1472
acme/storage#9066 merge conflict [open, 34d idle]
feat: per-test repeat pass rate threshold
https://github.com/acme/storage/pull/9066
Waiting on maintainers (2)
acme/gateway#1638 approved, not merged [open, 0d idle]
...
Already handled (1)
acme/serving#5636 closed by github-actions, resubmitted as #6421 [closed, 60d idle]
--json gibt die Liste mit den Feldern waiting_on, reason, approved und
idle_days aus.
Tests
Die Tests bauen Pull-Request-Datensätze in der Form, die die GraphQL-Abfrage
liefert, und geben sie an die Klassifizierung. Abgedeckt sind von Bots
geschlossene Pull Requests mit und ohne Branch oder Nachfolger, Schließungen
durch eine Person, gemergte Pull Requests, die offenen Zustände und Kommentare
von einer Person, einem Bot und dem Autor. Ein nachgebauter GitHub-Client
liefert Ergebnisseiten für die drei Suchen. Damit prüft ein Test, dass
Neueinreichungen verknüpft werden, die Ergebnisse sortiert sind und jede
Anfrage die eigenen Repositories des Nutzers ausschließt. Vier Tests decken die Regeln für Checks ab: den
Vercel-Status, einen abgebrochenen Job mit fehlschlagendem Gate-Job, einen
Gate-Job, der allein fehlschlägt, und einen echten Fehler neben einem Abbruch.
Die Tests greifen nicht aufs Netz zu. Das CI führt sie auf Python 3.10 bis
3.13 aus, prüft mit ruff und ruft --version und --help auf.
Grenzen
- Bots werden am Kontotyp und an einer kurzen Liste bekannter Bot-Konten erkannt. Schließt eine Person Pull Requests über ein persönliches Konto, zählt sie als Person.
- Ein Reopen kann trotzdem scheitern, wenn der Basis-Branch weitergezogen ist oder der Head-Branch per Force-Push überschrieben wurde. Ein neuer Pull Request, der den alten erwähnt, erscheint dann unter "Already handled".
- Ein Check, der wegen eines externen Timeouts fehlschlägt, wird als fehlschlagend gemeldet. Das Werkzeug kann ihn nicht von einem Fehler unterscheiden, den die Änderung verursacht.
- Die Suche von GitHub liefert höchstens 1000 Ergebnisse pro Anfrage.
Code und Installationsanleitung liegen unter github.com/Jwrede/pr-rescue.