Zelf AI pull request reviews draaien op eigen VPS
Host uw eigen AI PR-review-agent op een VPS voor volledige controle over uw code. Leer hoe u diff-scoped prompts, padfilters en inline commentaren configureert zonder dat data lekt.
Wat een zelfgehoste PR-review-agent doet
Een zelfgehoste PR-review-agent is een klein programma op een server die u beheert. Het leest de diff van een pull request (PR) en verstuurt alleen de gewijzigde regels naar een model. De reactie wordt geplaatst als inline review-commentaar. Het programma checkt nooit uw branch uit en leest nooit bestanden die niet door het pull request zijn aangeraakt. De enige inloggegevens die het programma bevat, zijn één API-sleutel voor het model en één token dat uitsluitend commentaar kan plaatsen en verder niets kan doen.
Een model kan een diff lezen. Dat onderdeel is opgelost. Waar het om gaat, is waar de diff naartoe gaat en wie de sleutel beheert. Bij een gehoste review-bot verlaat elke diff van elke private repository uw netwerk, komt deze in de logs van een derde partij terecht en valt deze onder hun bewaarbeleid. Op een VPS (virtual private server) die u beheert, gaat de diff van GitHub naar uw server en vervolgens naar de model-API. U kunt zelf de veertig regels code lezen die bepalen wat er wordt verstuurd.
Vereisten voor aanvang
- Een VPS met Ubuntu 24.04 waarop al een self-hosted GitHub Actions runner is geregistreerd bij de repository. Geef deze bij registratie het extra label
pr-reviewmee, aangezien de onderstaande workflow op dit label selecteert. - Een Anthropic API-sleutel uit de Claude Console.
- Een repository waarin u beheert wie een pull request mag openen. Een private repository is hierbij de eenvoudigste situatie. De sectie over forks hieronder behandelt de publieke situatie, waarbij de oplossing minder comfortabel is.
De reviewer installeren op de VPS
De runner-service draait onder het account zonder verhoogde rechten dat u aanmaakte bij het uitvoeren van ./svc.sh install. Installeer de reviewer onder datzelfde account, zodat de taak deze kan uitvoeren zonder sudo. Vervang runner hieronder door uw accountnaam.
sudo apt update && sudo apt install -y gh python3-venv
sudo install -d -m 755 -o runner -g runner /opt/pr-review
sudo -u runner python3 -m venv /opt/pr-review/venv
sudo -u runner /opt/pr-review/venv/bin/pip install anthropic
gh --versiongh --version geeft gh version 2.45.0 weer op Ubuntu 24.04 per augustus 2026. Elke release vanaf 2.20 beschikt over de --input-vlag die hieronder wordt gebruikt. Een Command 'gh' not found-melding betekent dat de universe-component niet is ingeschakeld; voer in dat geval sudo add-apt-repository universe uit en probeer het opnieuw.
Waar de key en het token zich bevinden
Twee geheimen, twee verschillende levenscycli. Geen van beide hoort thuis in de repository.
ANTHROPIC_API_KEY is een repository-geheim, in te stellen onder Settings, vervolgens Secrets and variables, en dan Actions. GitHub versleutelt dit en injecteert het tijdens runtime in de omgeving van de stap. Het is nooit een bestand op schijf en komt nooit voor in de git-geschiedenis.
GITHUB_TOKEN werkt anders. Actions maakt voor elke job een nieuw token aan en vernietigt dit zodra de job is voltooid. Wat dat token mag doen, wordt bepaald door het permissions:-blok in de workflow; dit is dus waar het principe van minimale privileges daadwerkelijk wordt toegepast:
permissions:
contents: read
pull-requests: writeDat token kan een review plaatsen. Het kan geen commit pushen, geen branch mergen, geen workflow-bestand bewerken en geen andere repository aanpassen. Een agent die kan reageren is een reviewer. Een agent die kan pushen is een committer, en daar is niemand mee akkoord gegaan. Behandel de model-key met dezelfde zorg, omdat deze geld uitgeeft op uw account. Er is meer informatie over dit type probleem te vinden in het buiten bereik houden van geheimen voor een AI-agent.
Actions vervangt de exacte geheime tekenreeks door *** in de job-logs. Dit werkt alleen bij een exacte overeenkomst van de tekenreeks; een key die u base64-codeert, over twee regels splitst of karakter voor karakter afdrukt, verschijnt dus in leesbare tekst. Voeg geen debug-stap toe die de omgeving dumpt.
Waarom een pull request vanuit een fork nooit uw API-sleutel ziet
De regel van GitHub is kort: met uitzondering van GITHUB_TOKEN worden secrets niet doorgegeven aan de runner wanneer een workflow wordt geactiveerd vanuit een geforkte repository. Een pull_request die vanuit een fork wordt uitgevoerd, start uw script dus zonder ANTHROPIC_API_KEY, waardoor de eerste API-aanroep faalt met invalid x-api-key.
De verleidelijke oplossing is om de trigger te wijzigen naar pull_request_target, die in de context van de basis-repository draait en wel toegang heeft tot de secrets. Doe dit hier niet. De eigen beveiligingsrichtlijnen van GitHub stellen dat dergelijke workflows "bevoorrecht zijn, wat betekent dat ze dezelfde cache van de main-branch delen met andere bevoorrechte workflow-triggers, en mogelijk schrijftoegang tot de repository en toegang tot gerefereerde secrets hebben", en dat het resultaat "kan worden misbruikt om een repository over te nemen".
Dezelfde richtlijnen zijn duidelijk over de runner: "Self-hosted runners zouden bijna nooit gebruikt moeten worden voor publieke repositories op GitHub, omdat elke gebruiker pull requests naar de repository kan openen en de omgeving kan compromitteren."
Dit leidt tot twee ontwerpkeuzes. De job bevat een beveiliging zodat deze alleen draait op branches die naar uw eigen repository zijn gepusht. Daarnaast bevat de workflow helemaal geen actions/checkout-stap. De agent heeft de branch nooit op schijf staan, waardoor een vijandig pull request slechts tekst is die naar een model wordt gestuurd. Het kan geen build-script op uw VPS uitvoeren, omdat niets op uw VPS het ooit uitvoert. Tekst is echter niet hetzelfde als ongevaarlijk: een diff geschreven door een vreemde is niet-vertrouwde input die bij een model aankomt. Dit is dezelfde vertrouwensgrens die u tegenkomt wanneer u een agent webzoekopdrachten laat uitvoeren, en het enige dat het hier inperkt, is dat deze agent niets anders kan doen dan een reactie plaatsen.
Haal de diff op, niet de repository
Eén verzoek levert de volledige diff op als platte tekst.
export GH_TOKEN=your_token # in the workflow this comes from secrets.GITHUB_TOKEN
gh api /repos/OWNER/REPO/pulls/42 -H "Accept: application/vnd.github.diff"Het Accept: application/vnd.github.diff media-type zorgt ervoor dat het antwoord verandert van een JSON-object dat de pull request beschrijft naar de unified diff zelf, en gh api print die body ongewijzigd. De eerste regel die u ziet, moet beginnen met diff --git a/. Een gh: Not Found (HTTP 404) betekent dat het token de repository niet kan inzien, wat bij een fine-grained personal token bijna altijd betekent dat de Pull requests-toestemming niet is ingeschakeld.
Filter voordat u tokens verbruikt
Dit onderdeel maakt het verschil tussen een bot die mensen lezen en een bot die mensen dempen. Elk onderstaand filter wordt uitgevoerd voordat het model ook maar één byte verwerkt.
- Padfilters. Sluit lock-bestanden, vendored-mappen, geminificeerde bundels en gegenereerde code uit. Commentaar van een model op
package-lock.jsonis pure ruis, en dergelijke bestanden beslaan vaak het grootste deel van de bytes in een diff. - Een groottelimiet. Boven de limiet wordt de review overgeslagen en wordt het proces succesvol afgesloten. Een refactor van 4000 regels krijgt één eerlijke melding dat het bestand te groot was voor een automatische review, in plaats van zestig gokken.
- Een ernst-drempel en een limiet op het aantal opmerkingen. Rapporteer bevindingen met een hoge en gemiddelde ernst, tot maximaal tien stuks, gesorteerd op ernst. Niemand leest de elfde opmerking.
Het script
Sla dit op als /opt/pr-review/review.py. Het leest de configuratie vanuit de omgeving, waardoor de workflow van model kan wisselen zonder dat de code hoeft te worden aangepast.
#!/usr/bin/env python3
"""Review only the changed lines of one pull request."""
import json
import os
import subprocess
import sys
import anthropic
REPO = os.environ["GITHUB_REPOSITORY"]
PR = os.environ["PR_NUMBER"]
MODEL = os.environ.get("REVIEW_MODEL", "claude-haiku-4-5-20251001")
MAX_DIFF_BYTES = int(os.environ.get("MAX_DIFF_BYTES", "120000"))
MIN_SEVERITY = os.environ.get("MIN_SEVERITY", "medium")
MAX_COMMENTS = 10
RANK = {"low": 0, "medium": 1, "high": 2}
SKIP = ("package-lock.json", "poetry.lock", "/vendor/", "/node_modules/", ".min.js")
raw_diff = subprocess.run(
["gh", "api", f"/repos/{REPO}/pulls/{PR}",
"-H", "Accept: application/vnd.github.diff"],
check=True, capture_output=True, text=True,
).stdoutHet opsplitsen van de diff per bestand maakt padfiltering mogelijk. Het nummeren van elke regel zorgt ervoor dat de review-opmerkingen op de juiste plek terechtkomen. GitHub accepteert een inline-opmerking alleen op een regel die deel uitmaakt van de diff, dus het model moet een bestaand regelnummer citeren. Door het de nummers aan te reiken, kan het model een nummer kopiëren in plaats van er zelf een te verzinnen.
def per_file(diff_text):
"""Split a unified diff into one string per file."""
sections, current = [], []
for line in diff_text.splitlines():
if line.startswith("diff --git ") and current:
sections.append("\n".join(current))
current = []
current.append(line)
if current:
sections.append("\n".join(current))
return sections
def annotate(section):
"""Prefix every line that exists in the new file with its line number."""
out, n, in_hunk = [], 0, False
for line in section.splitlines():
if line.startswith("@@"):
n = int(line.split("+")[1].split(",")[0].split(" ")[0])
in_hunk = True
out.append(line)
elif not in_hunk or line.startswith(("-", "\\")):
out.append(line)
else:
out.append(f"{n}\t{line}")
n += 1
return "\n".join(out)
kept = [s for s in per_file(raw_diff)
if not any(p in s.split("\n", 1)[0] for p in SKIP)]
payload = "\n".join(annotate(s) for s in kept)
if not payload.strip():
print("every changed file was filtered out")
raise SystemExit(0)
if len(payload) > MAX_DIFF_BYTES:
print(f"diff is {len(payload)} bytes, over the {MAX_DIFF_BYTES} cap")
raise SystemExit(0)De hunk-header bevat de nummering. @@ -12,7 +12,9 @@ geeft aan dat de hunk van het nieuwe bestand begint op regel 12, dus de teller begint daar en loopt op bij toegevoegde en ongewijzigde regels. Verwijderde regels worden niet genummerd, omdat deze niet in het nieuwe bestand voorkomen. De controle op regels die beginnen met een backslash slaat de no-newline-markering over die git aan het einde van een bestand schrijft; zonder deze controle zou elk volgend nummer met één worden verschoven.
Beide exits gebruiken status 0, niet 1. Een gefilterde of te grote pull request moet een groen vinkje tonen. Een rood vinkje waar een mens niets mee kan, wordt genegeerd, en zodra één controle wordt genegeerd, worden ze allemaal genegeerd.
SYSTEM = (
"You review one pull request diff. Every line that exists in the new file is "
"prefixed with its line number and a tab character. "
"Report only defects you can see in the lines shown: a crash, a resource leak, "
"a security mistake, a wrong boundary condition, a broken contract with code "
"that is visible in this diff. Do not comment on style, naming or formatting. "
"Do not guess about code you cannot see. Leave out anything you are not "
"certain about. An empty findings list is a normal and common answer. "
'Reply with JSON only, in this shape: {"findings": [{"path": "src/app.py", '
'"line": 42, "severity": "high", "comment": "what is wrong, then why"}]} '
"Every line number must be one you can see in the left column of that file."
)
client = anthropic.Anthropic()
message = client.messages.create(
model=MODEL,
max_tokens=2000,
system=SYSTEM,
messages=[{"role": "user", "content": payload}],
)
print(f"stop={message.stop_reason} in={message.usage.input_tokens} "
f"out={message.usage.output_tokens}", file=sys.stderr)
text = message.content[0].text
findings = json.loads(text[text.find("{"):text.rfind("}") + 1])["findings"]
findings = [f for f in findings if RANK.get(f["severity"], 0) >= RANK[MIN_SEVERITY]]
findings.sort(key=lambda f: -RANK.get(f["severity"], 0))
del findings[MAX_COMMENTS:]
if not findings:
print("nothing above the severity threshold; posting no comment")
raise SystemExit(0)
review = {
"event": "COMMENT",
"body": f"Automated review of the changed lines. {len(findings)} finding(s).",
"comments": [
{"path": f["path"].removeprefix("b/"), "line": f["line"], "side": "RIGHT",
"body": f"**{f['severity']}** {f['comment']}"}
for f in findings
],
}
subprocess.run(
["gh", "api", "-X", "POST", f"/repos/{REPO}/pulls/{PR}/reviews", "--input", "-"],
input=json.dumps(review), text=True, check=True,
)Drie details in dat blok zijn essentieel. De JSON wordt gesneden tussen de eerste { en de laatste }, omdat een model zijn antwoord soms in een code-blok plaatst en json.loads vastloopt op dat blok. De path wordt ontdaan van een voorloop-b/, aangezien dat voorvoegsel uit de diff-header komt en GitHub een pad relatief aan de repository verwacht. En --input - verstuurt de volledige review als één API-aanroep, zodat tien bevindingen als één melding aankomen in plaats van als tien afzonderlijke meldingen.
Wanneer er niets te rapporteren valt, plaatst het script niets. Een bot die bij elke pull request "geen problemen gevonden" schrijft, leert mensen om er overheen te lezen, waardoor ze ook de melding missen die er wel toe deed.
Integreer het in de workflow
Sla dit op als .github/workflows/pr-review.yml:
name: pr-review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
pull-requests: write
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
if: github.event.pull_request.head.repo.full_name == github.repository && !contains(github.event.pull_request.labels.*.name, 'no-ai-review')
runs-on: [self-hosted, linux, pr-review]
steps:
- name: Review the changed lines
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REVIEW_MODEL: claude-haiku-4-5-20251001
MIN_SEVERITY: medium
run: /opt/pr-review/venv/bin/python /opt/pr-review/review.pyGITHUB_REPOSITORY staat niet in dat env:-blok omdat Actions dit al voor elke job instelt. De concurrency-groep is van belang voor de kosten: zonder deze groep leiden drie snelle fixes naar een branch tot drie volledige reviews waarvoor u betaalt, terwijl er met deze groep alleen de laatste overblijft.
De regel if: voert twee taken uit. De eerste helft slaat pull requests van forks over, die zonder sleutel toch zouden falen. De tweede helft geeft uw team een noodstop: voeg het label no-ai-review toe aan een pull request en de job wordt niet uitgevoerd.
Open een pull request en bekijk wat er gebeurt:
gh run list --workflow=pr-review.yml --limit 3
gh run view --log
gh pr view 42 --commentsEen run die binnen enkele seconden eindigt met nothing above the severity threshold; posting no comment in het logbestand, werkt correct. Bij een klein, overzichtelijk pull request is dat het verwachte resultaat.
Wat kost een geautomatiseerde pull request review?
De diff vormt vrijwel de gehele input, dus de omvang van de diff bepaalt de prijs. Hieronder staat een gemeten diff van 500 regels plus de system prompt, geteld met het token-tel-eindpunt in plaats van een schatting.
The data behind this chart
[
{
"label": "Haiku 4.5",
"input_tokens": "8,000",
"output_tokens": "1,200"
},
{
"label": "Sonnet 5",
"input_tokens": "10,400",
"output_tokens": "1,560"
},
{
"label": "Opus 5",
"input_tokens": "10,400",
"output_tokens": "1,560"
}
]Die diff kwam uit op 8,000 input-tokens op Haiku 4.5 en 10,400 op Sonnet 5. Dezelfde tekst, een ander aantal. Claude-modellen vanaf 4.7 gebruiken een nieuwere tokenizer die ongeveer 30% meer tokens genereert voor dezelfde input, wat Anthropic documenteert op zijn prijspagina. Houd hier rekening mee wanneer u een nieuwer model vergelijkt met een ouder model op basis van de prijs per miljoen tokens.
Catalogusprijzen per augustus 2026: Haiku 4.5 kost $1 per miljoen input-tokens en $5 per miljoen output-tokens. Sonnet 5 kost $2 en $10 onder het introductietarief dat loopt tot 31 augustus 2026, daarna $3 en $15. Opus 5 kost $5 en $25.
The data behind this chart
[
{
"label": "Haiku 4.5",
"cost_per_pr_cents": 1.4,
"cost_200_prs_usd": "2.80"
},
{
"label": "Sonnet 5",
"cost_per_pr_cents": 3.64,
"cost_200_prs_usd": "7.28"
},
{
"label": "Opus 5",
"cost_per_pr_cents": 9.1,
"cost_200_prs_usd": "18.20"
}
]Dat is 1.4 cent per pull request op Haiku 4.5 en 9.1 cent op Opus 5. Een team dat 200 pull requests per maand samenvoegt, betaalt ongeveer $2.80 op Haiku 4.5, $7.28 op Sonnet 5, of $18.20 op Opus 5. Vermenigvuldig vanaf 1 september 2026 de rij voor Sonnet 5 met 1,5.
Twee factoren kunnen de werkelijke rekening hoger laten uitvallen dan deze schatting. De synchronize activeert reviews bij elke push, dus een actieve branch met acht pushes kost acht reviews, en de concurrency-regel helpt alleen wanneer pushes kort na elkaar plaatsvinden. De cijfers gaan er ook vanuit dat de padfilters werken: één ongefilterd lock-bestand kan de input op zichzelf verdubbelen.
Prompt caching helpt hier niet. Het gecachte voorvoegsel moet byte-identiek zijn tussen aanroepen, en de diff is elke keer anders. De system prompt is het enige stabiele onderdeel, en deze blijft ver onder de minimale cachebare lengte. Zie voor de algemene regel wanneer prompt caching zichzelf terugverdient, en voor het kiezen tussen de drie bovenstaande modellen, welk Claude-model te gebruiken voor welke taak.
Meet uw eigen diffs voordat u het inschakelt
Voeg deze regel toe nadat payload is gebouwd en voer het script vervolgens handmatig uit tegen een paar pull requests van de afgelopen maand:
print(client.messages.count_tokens(
model=MODEL, system=SYSTEM, messages=[{"role": "user", "content": payload}]
).input_tokens)Het tel-eindpunt voert het model niet uit, dus het verbruikt geen input- of output-tokens, en het gebruikt de tokenizer die bij het opgegeven model hoort. Voer dit uit over tien echte pull requests uit uw eigen repository en neem de mediaan in plaats van het gemiddelde, zodat één enorme migratie de schatting niet vertekent.
Waarom review-bots worden gedempt en hoe u dit voorkomt
Twee gedragingen schaden het vertrouwen in deze bots, en beide kunnen worden opgelost met de bovenstaande code.
Alles tegelijk beoordelen. Een bot die veertig opmerkingen achterlaat, krijgt er nul gelezen. De drempelwaarde voor ernst en de limiet van tien opmerkingen zijn geen kwestie van beleefdheid; ze zorgen ervoor dat de werkelijke bevindingen zichtbaar blijven. Door te sorteren op ernst voordat de lijst wordt ingekort, zorgt u ervoor dat de minst belangrijke bevindingen worden weggelaten in plaats van een willekeurige selectie van tien.
Met stelligheid reageren op zaken die niet gecontroleerd kunnen worden. Dit is de reden waarom engineers de bot definitief uitschakelen. Een model dat 200 regels van een codebase van 40.000 regels ziet, zal nog steeds schrijven "dit breekt de cache-invalidation in redis_client.py" over een bestand dat het nooit heeft gezien. De system prompt stuurt hier in duidelijke taal op aan: rapporteer alleen defecten die zichtbaar zijn in de getoonde regels en laat alles weg waar u niet zeker van bent. Het direct benoemen van de fout werkt beter dan in algemene termen om nauwkeurigheid vragen. Het model vertellen dat een leeg resultaat normaal is, voorkomt dat het iets verzint bij een wijziging van twee regels.
Plaats de review als COMMENT, nooit als REQUEST_CHANGES. De mening van een model mag een merge niet kunnen blokkeren. Zodra dit wel kan, zal iemand met een deadline de hele workflow verwijderen in plaats van de discussie aan te gaan.
Foutmodi en de bijbehorende meldingen
HTTP 422 bij het plaatsen van de review. gh print gh: Unprocessable Entity (HTTP 422) en de body van de response benoemt het veld: Pull request review thread line must be part of the diff. GitHub kan die commentaar niet verankeren. De gebruikelijke oorzaken zijn een door het model verzonnen regelnummer, een path die nog steeds het b/-voorvoegsel bevat, of een commentaar op een verwijderde regel, waarvoor side moet worden ingesteld op LEFT in plaats van RIGHT. Print de review-JSON voordat u deze plaatst en controleer handmatig één commentaar tegen de diff.
invalid x-api-key van de model-API. De stap faalt bij de eerste messages.create-aanroep. Of het ANTHROPIC_API_KEY-geheim is niet ingesteld in de repository, of de pull request is afkomstig van een fork waardoor Actions helemaal geen geheimen heeft doorgegeven. De fork-beveiliging in de if:-regel had dit moeten overslaan, dus controleer die regel eerst.
gh: Resource not accessible by integration (HTTP 403). Het job-token heeft geen schrijfrechten voor pull requests. Voeg pull-requests: write toe aan het permissions:-blok. Als dit er al staat, kijk dan bij Settings, vervolgens Actions en daarna General, waar een organisatiebeleid kan beperken wat een workflow-token mag aanvragen.
json.decoder.JSONDecodeError. Het model heeft geen parseerbare JSON geretourneerd. De meest voorkomende oorzaak is een response die de tokenlimiet heeft bereikt en halverwege een object is gestopt. De logregel print stop_reason voor precies dit probleem: een waarde van max_tokens betekent dat u max_tokens moet verhogen of MAX_COMMENTS moet verlagen.
De workflow start nooit. gh run list toont niets voor de pull request. Controleer of paths-ignore niet elk gewijzigd bestand heeft gefilterd, controleer daarna de fork-beveiliging en de label-beveiliging, en kijk of de runner actief is met sudo systemctl status 'actions.runner.*' op de VPS. Een offline runner laat de job in de wachtrij staan zonder enige foutmelding in de pull request.
Elke review komt leeg terug. Stel MIN_SEVERITY in op low voor één run. Als er bevindingen verschijnen, doet de drempelwaarde zijn werk. Als er niets verschijnt, print dan payload en bevestig dat de filters niet de volledige diff hebben verwijderd.
Naast uw andere agents draaien
De reviewer is klein, dus het is verleidelijk om deze op de server te plaatsen waar al uw andere processen draaien. Houd de omgeving echter gescheiden als de repository van belang is. Dit proces beheert een token waarmee code kan worden becommentarieerd en een sleutel waarmee kosten kunnen worden gemaakt. Een self-hosted runner is per definitie een plek waar workflow-code wordt uitgevoerd. Een toegewezen account zonder privileges zonder sudo-rechten, op een host die verder niets uitvoert, vormt de basis. Als u ook interactieve agents draait die code uitchecken, is een tijdelijke VM per agent de aanbevolen werkwijze, en een coding agent op een VPS draaien beschrijft de algemene configuratie. Als de Anthropic API nieuw voor u is, is een eerste Claude API-app op een VPS een kleinschaliger startpunt dan dit.
FAQ
Heeft een AI PR-review-agent schrijftoegang nodig tot mijn repository?
Nee. De agent heeft pull-requests: write nodig om een review te plaatsen en contents: read om de diff op te halen. Dat is de volledige lijst; u stelt dit in binnen het permissions:-blok van de workflow, wat de rechten van de per-job GITHUB_TOKEN beperkt. Met deze twee regels kan de agent commentaar geven op een pull request, maar kan deze geen commit pushen of een branch mergen. Plaats reviews met event: COMMENT in plaats van REQUEST_CHANGES, zodat de agent ook geen merge kan blokkeren.
Waarom mislukt mijn review-commentaar met HTTP 422?
GitHub accepteert een inline review-commentaar alleen op een regel die deel uitmaakt van de pull request-diff; bij een afwijking wordt Pull request review thread line must be part of the diff geretourneerd. Controleer of path relatief is aan de repository zonder b/-prefix uit de diff-header, en of het regelnummer voorkomt in een hunk van dat bestand. side moet RIGHT zijn voor een toegevoegde of ongewijzigde regel en LEFT voor een verwijderde regel. Door elke regel van de diff te voorzien van het regelnummer van het nieuwe bestand voordat deze naar het model wordt verzonden, voorkomt u dat het model zelf nummers gaat verzinnen.
Kan ik dit uitvoeren op een publieke repository met pull requests vanuit forks?
Niet met dit ontwerp. GitHub geeft geen secrets door aan een workflow die wordt getriggerd vanuit een fork, waardoor de model-key ontbreekt en de uitvoering mislukt. GitHub stelt bovendien dat self-hosted runners "vrijwel nooit gebruikt mogen worden voor publieke repositories", omdat iedereen een pull request kan openen die code op uw machine laat draaien. Voor een publiek project kunt u de reviewer beperken tot branches die naar de repository zelf zijn gepusht, wat de if:-guard doet, of de review-stap verplaatsen naar een GitHub-hosted runner en accepteren dat de diff uw eigen infrastructuur verlaat.
Welk model moet ik gebruiken voor pull request-review?
Begin met Haiku 4.5. Het lezen van een begrensde diff tegen een vaste lijst van defecttypes is geen complex redeneerprobleem, en het goedkoopste model houdt de maandelijkse kosten op een niveau waar niemand over valt. Stap over naar Sonnet 5 als u merkt dat het model echte bugs in uw taal of framework mist; meet dit resultaat in plaats van er zomaar vanuit te gaan. Opus 5 is per pull request veruit het duurst van de drie, wat makkelijker te rechtvaardigen is op een release-branch dan bij elke push naar elke feature-branch.