Od zgłoszenia do pull requesta bez rąk na klawiaturze
Potok od zgłoszenia do PR zamienia zgłoszenie na GitHubie oznaczone etykietą w szkic pull requesta bez niczyjego pisania: agent (Claude Code, Codex lub Cursor) implementuje zgłoszenie w izolowanym przebiegu, CI ponownie uruchamia bramki, które agent deklaruje jako zaliczone, pętla poprawek z limitem trzech prób naprawia błędy, a pakiet dowodów przygotowuje pull request do merge’a przez człowieka.
Masz backlog dobrze opisanych zgłoszeń: zmienić nazwę pola, dodać filtr do endpointu, naprawić błąd ze znanym sposobem reprodukcji. Każde potrafi zjeść inżynierowi większość godziny na przygotowania, pisanie i czekanie na CI, a pisanie mógłby przejąć agent. Problemem jest wszystko wokół pisania. Ktoś musi uruchomić przebieg, pilnować go, wypchnąć gałąź, otworzyć pull request, ponowić niestabilny test i ocenić, czy zielony znaczek cokolwiek znaczy.
Ten tutorial buduje wersję, w której nikt nie robi tych rzeczy ręcznie. Jest dla dewelopera, który to podłącza, i dla tech leada, który decyduje, jakie zgłoszenia mogą do potoku trafić. Każdy plik jest kompletny i przeznaczony do skopiowania do repozytorium.
Co daje ci potok od zgłoszenia do PR
Dział zatytułowany „Co daje ci potok od zgłoszenia do PR”- Etykietę
agent:ready, która uruchamia agenta na runnerze GitHuba albo w Cloud Agencie Cursora, nigdy na laptopie. - Kontrakt zadania: formularz zgłoszenia, prompt, którego przebieg zawsze używa, i jeden skrypt definiujący „gotowe”.
- Podział uprawnień: agent edytuje pliki, workflow commituje, CI ocenia, a człowiek robi merge.
- Pętlę review i poprawek, która zatrzymuje się po trzech próbach i przekazuje pull request wskazanej osobie.
- Komentarz z pakietem dowodów w każdym pull requeście agenta, wygenerowany przez CI, a nie przez agenta.
- Pięć liczb, po których tech lead pozna, czy potok produkuje kod nadający się do merge’a.
Jak działa potok od zgłoszenia do PR?
Dział zatytułowany „Jak działa potok od zgłoszenia do PR?”Potok ma sześć etapów. Najważniejsza jest trzecia kolumna: na żadnym etapie komponent, który pisze kod, nie decyduje zarazem, że kod jest dobry.
| Etap | Co działa | Kto ma uprawnienia | Co go zatrzymuje |
|---|---|---|---|
| 1. Etykieta | Maintainer nadaje agent:ready | Człowiek z prawem zapisu | Brak etykiety albo brak prawa zapisu u osoby, która ją nadała |
| 2. Izolowany przebieg | Agent na świeżym runnerze albo w maszynie wirtualnej Cloud Agenta | Agent może tylko edytować pliki | Limit tur, limit kwoty, timeout joba albo warunek stopu w prompcie |
| 3. Commit i szkic PR | Job workflow z tokenem aplikacji GitHub | Workflow, nigdy agent | Pusty patch oznacza zgłoszenie etykietą agent:needs-human |
| 4. Bramki | scripts/agent-gates.sh z gałęzi bazowej, uruchomiony w CI na kodzie pull requesta | CI, w jobie bez sekretów i bez tokenu z prawem zapisu | Każda niezaliczona bramka, w tym zmiana w chronionej ścieżce |
| 5. Pętla review i poprawek | Znów agent, z logami bramek i komentarzami z review na wejściu | Maksymalnie trzy commity Agent-Fix: | Limit albo warunek stopu nadaje PR etykietę agent:needs-human |
| 6. Merge | Człowiek czyta pakiet dowodów i zatwierdza | Właściciel zgłoszenia oraz CODEOWNERS dla wrażliwych ścieżek | Ruleset wymagający jednej akceptacji i sprawdzenia evidence |
Decyzję projektową stojącą za etapami 3 i 4 warto nazwać wprost: żaden job, który uruchamia kod napisany przez agenta, nie ma tokenu z prawem zapisu. Job agenta robi checkout bez zapisanych poświadczeń gita i przekazuje akcji Claude Code własny GITHUB_TOKEN joba, który przy contents: read służy tylko do odczytu. Przekazuje patch drugiemu jobowi i dopiero ten job tworzy token z prawem zapisu. W CI job, który uruchamia kod pull requesta, nie ma sekretów, a job, który komentuje i oznacza pull request jako gotowy, nigdy tego kodu nie pobiera ani nie uruchamia. Agent z wstrzykniętym promptem może więc zepsuć własną kopię roboczą i wydać swój budżet, ale nie wypchnie niczego na gałąź, nie napisze komentarza jako bot i nie dotknie innego repozytorium. Trzyma jednak klucz API modelu, gdy uruchamia kod, który przed chwilą napisał, więc daj potokowi osobny klucz z limitem wydatków.
Które narzędzie powinno wykonywać krok agenta?
Dział zatytułowany „Które narzędzie powinno wykonywać krok agenta?”Między trzema narzędziami różni się tylko krok agenta. Kontrakt zadania, bramki, dowody i pętla są identyczne, więc możesz zmienić narzędzie albo uruchomić dwa równolegle, żeby je porównać.
| Claude Code | Codex | Cursor | |
|---|---|---|---|
| Gdzie działa | anthropics/claude-code-action@v1 na runnerze GitHuba | openai/codex-action@v1 (tag v1.12) na runnerze GitHuba | W Cloud Agencie, który według Cursora działa „in isolated VMs in the cloud with full development environments” (sprawdzone 2026-08-28) |
| Kto uruchamia | Workflow, w trybie automatyzacji (wejście prompt) | Workflow | Workflow, przez @cursor/sdk 1.0.32 |
| Ograniczenia | Lista dozwolonych narzędzi --allowedTools, --max-turns, --max-budget-usd | permission-profile: ":workspace" bez dostępu do sieci oraz safety-strategy: drop-sudo | Maszyna wirtualna Cursora; SDK nie udostępnia listy dozwolonych narzędzi dla agentów w chmurze |
| Kto otwiera PR | Twój workflow | Twój workflow | Cursor (autoCreatePR: true); twój workflow nadaje etykietę |
| Rozliczenie | Minuty Actions plus tokeny API albo subskrypcja przez CLAUDE_CODE_OAUTH_TOKEN | Minuty Actions plus tokeny API (sekret OPENAI_API_KEY) | Zużycie Cursora na koncie, do którego należy CURSOR_API_KEY |
Wszystkie trzy używają domyślnego modelu narzędzia, chyba że ustawisz inny. Aktualne modele domyślne i ceny są w przeglądzie modeli; zacznij od nich, zamiast przypinać model w workflow.
Przygotuj repozytorium raz
Dział zatytułowany „Przygotuj repozytorium raz”-
Utwórz trzy etykiety.
agent:readyuruchamia przebieg,agent-proznacza pull requesty obsługiwane przez pętlę, aagent:needs-humanzatrzymuje pętlę. -
Utwórz małą aplikację GitHub do wypychania zmian. Nadaj jej Contents: read and write, Pull requests: read and write i Issues: read and write, zainstaluj ją w repozytorium, zapisz jej client ID jako zmienną Actions
AGENT_APP_CLIENT_ID, a klucz prywatny jako sekretAGENT_APP_PRIVATE_KEY. Aplikacja jest potrzebna, bo GitHub nie uruchamia workflow ze zdarzeń utworzonych domyślnymGITHUB_TOKEN: pull request otwarty tym tokenem nigdy nie uruchomiłby CI. Traktuj aplikację jak tożsamość agenta z własnym właścicielem; rotację i zakres opisuje tożsamość agentów i sekrety. -
Dodaj sekret swojego narzędzia.
ANTHROPIC_API_KEY(alboCLAUDE_CODE_OAUTH_TOKEN, wygenerowany poleceniemclaude setup-token),OPENAI_API_KEYlubCURSOR_API_KEY. Dla Claude Code ten potok nie potrzebuje aplikacji Claude GitHub: workflow przekazują akcji tylko do odczytuGITHUB_TOKENjoba jakogithub_token. Bez tego wejścia akcja uwierzytelnia się przez aplikację, której token może zapisywać zawartość repozytorium. -
Zabezpiecz domyślną gałąź rulesetem. Wymagaj pull requesta, jednej akceptacji, review od właścicieli kodu oraz sprawdzeń
evidencei twojego zwykłego CI. Zablokuj force push. Potem skieruj przezCODEOWNERSklasy zmian, które człowiek musi przeczytać, łącznie z każdym plikiem, który decyduje, co znaczy „przechodzi”:# .github/CODEOWNERS: paths the pipeline may change but never merge alone/src/auth/ @acme/security/src/billing/ @acme/payments/migrations/ @acme/data/tests/contract/ @acme/tech-leads/.github/ @acme/tech-leads# The rules the agent is judged by, and the settings its runs load/scripts/agent-gates.sh @acme/tech-leads/scripts/evidence.sh @acme/tech-leads/package.json @acme/tech-leads/package-lock.json @acme/tech-leads/tsconfig*.json @acme/tech-leads/vite.config.* @acme/tech-leads/vitest.config.* @acme/tech-leads/eslint.config.* @acme/tech-leads/.claude/ @acme/tech-leads/.codex/ @acme/tech-leads/.cursor/ @acme/tech-leads/.mcp.json @acme/tech-leads/AGENTS.md @acme/tech-leads/CLAUDE.md @acme/tech-leads -
Wyklucz katalog roboczy agenta. Dodaj
.agent/do.gitignore. Plik zadania, logi bramek i raport agenta żyją tam i nigdy nie mogą trafić do commita.
Napisz kontrakt zadania: formularz zgłoszenia, skrypt bramek i schemat wyniku
Dział zatytułowany „Napisz kontrakt zadania: formularz zgłoszenia, skrypt bramek i schemat wyniku”Trzy pliki definiują, o co agent jest proszony i co znaczy „gotowe”. Zmieniają się rzadko i to je tech lead przegląda, kiedy potok działa źle.
Formularz zgłoszenia wymusza pola, od których zależy prompt. Rada DORA o małych partiach pasuje tu wprost: „AI can easily generate massive blocks of code, which are hard to review and test. Enforcing the discipline of small batches counteracts this risk” (DORA, blog Google Cloud, 2025-12-10). Jeden rezultat na zgłoszenie sprawia, że każdy pull request da się ocenić na podstawie samych dowodów.
name: Agent-ready taskdescription: One outcome, provable by checks. Label agent:ready only after review.body: - type: textarea id: outcome attributes: label: Outcome description: One sentence a reviewer can verify. validations: { required: true } - type: textarea id: criteria attributes: label: Acceptance criteria description: "One per line: Given / when / then -> check: <test file or command>" validations: { required: true } - type: textarea id: scope attributes: label: Allowed paths description: Globs the agent may change, for example src/invoices/** validations: { required: true } - type: textarea id: out-of-scope attributes: label: Out of scope description: What must not change, even if it looks related.Skrypt bramek to jedyna definicja „gotowe”. Agent uruchamia go w swoim przebiegu, a CI jeszcze raz, niezależnie; liczy się tylko wynik z CI. CI zawsze uruchamia kopię skryptu z gałęzi bazowej na kodzie pull requesta, więc agent, który zmieni skrypt, package.json (którego skrypty lint, typecheck i test wywołują bramki) albo konfigurację testów, nie zmieni sposobu, w jaki jest oceniany, a za samą próbę obleje strażnika wyroczni. Tak potok chroni wyrocznię przed agentem, którego ta wyrocznia ocenia.
#!/usr/bin/env bash# scripts/agent-gates.sh <base-ref># Writes .agent/gates.md and exits 0 only when every gate passes.set -uo pipefailBASE="${1:?usage: agent-gates.sh <base-ref>}"# The protected-path pattern lives next to the prompts, in .github/agent/, which is itself protected.PROTECTED=$(cat "$(dirname "$0")/../.github/agent/protected-paths.txt")mkdir -p .agentout=.agent/gates.mdprintf '| Gate | Result | Command |\n| --- | --- | --- |\n' > "$out"fail=0
gate() { local name="$1"; shift if "$@" > ".agent/$name.log" 2>&1; then echo "| $name | pass | \`$*\` |" >> "$out" else echo "| $name | **fail (exit $?)** | \`$*\` |" >> "$out"; fail=1 fi}
# Diff from the merge base, not the base tip: commits that landed on the base# branch after the PR forked are not the agent's. No merge base fails closed.if ! mb=$(git merge-base "$BASE" HEAD); then echo "| oracle-guard | **fail** | no merge base with $BASE |" >> "$out"; exit 1fi# Committed, uncommitted and new files, so it works before and after the commit.changed=$( { git diff --name-only "$mb"; git ls-files --others --exclude-standard; } | sort -u)protected=$(printf '%s\n' "$changed" | grep -E "$PROTECTED" || true)if [ -n "$protected" ]; then echo "| oracle-guard | **fail** | touched: $(echo $protected) |" >> "$out"; fail=1else echo "| oracle-guard | pass | no protected path touched |" >> "$out"fi
gate lint npm run lintgate typecheck npm run typecheckgate test npm testexit $failWzorzec chronionych ścieżek to jedno rozszerzone wyrażenie regularne w .github/agent/protected-paths.txt, czyli w pliku leżącym w chronionym katalogu. Czytają go skrypt bramek i pętla poprawek, więc lista żyje w jednym miejscu:
^(\.github/|\.claude/|\.codex/|\.cursor/|\.mcp\.json$|AGENTS\.md$|CLAUDE\.md$|tests/contract/|migrations/|scripts/(agent-gates|evidence)\.sh$|package(-lock)?\.json$|tsconfig[^/]*\.json$|(vite|vitest|eslint)\.config\.[^/]+$)Dopisz do niego własne pliki konfiguracji testów i lintera i trzymaj go w zgodzie z blokiem CODEOWNERS powyżej.
Schemat wyniku daje każdemu przebiegowi ten sam, czytelny maszynowo raport własny. Codex wymusza go przez --output-schema; Claude Code i Cursor dostają prośbę o jego zachowanie w prompcie. Workflow wypisuje raport agenta w opisie pull requesta pod nagłówkiem, który mówi wprost, że to deklaracja, a nie dowód.
{ "type": "object", "additionalProperties": false, "required": ["status", "stop_reason", "items"], "properties": { "status": { "enum": ["done", "stopped"] }, "stop_reason": { "type": "string" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["claim", "check", "result"], "properties": { "claim": { "type": "string" }, "check": { "type": "string" }, "result": { "enum": ["pass", "fail", "not-run"] } } } } }}Zapisz go jako .github/agent/result.schema.json.
Prompty do skopiowania dla przebiegu bez nadzoru
Dział zatytułowany „Prompty do skopiowania dla przebiegu bez nadzoru”Potok używa dwóch promptów trzymanych w repozytorium, więc każda ich zmiana przechodzi review jak zwykły kod. Oba traktują zgłoszenie i uwagi z review jako dane, bo każdy, kto może edytować zgłoszenie, może też podsunąć agentowi dowolny tekst.
Trzeci prompt jest dla osoby, która nadaje etykietę. Uruchom go lokalnie w dowolnym z trzech narzędzi, zanim oznaczysz zgłoszenie. Wyłapuje zgłoszenia, które zużyłyby trzy próby poprawek i i tak skończyły z etykietą agent:needs-human.
Uruchom agenta po nadaniu etykiety zgłoszeniu
Dział zatytułowany „Uruchom agenta po nadaniu etykiety zgłoszeniu”Poniższy workflow jest kompletny dla Claude Code. Wersje dla Codeksa i Cursora zastępują tylko krok oznaczony AGENT STEP, jak pokazują zakładki pod nim.
name: agent-issueon: issues: types: [labeled]
concurrency: group: agent-issue-${{ github.event.issue.number }} cancel-in-progress: false
jobs: agent: if: github.event.label.name == 'agent:ready' runs-on: ubuntu-latest timeout-minutes: 45 permissions: contents: read # the only token in this job is read-only steps: - uses: actions/checkout@v7 with: fetch-depth: 0 persist-credentials: false # the agent gets no git credentials - uses: actions/setup-node@v7 with: { node-version: 22, cache: npm } - run: npm ci # install before the agent runs; Codex's :workspace has no network
- name: Build the task file (issue text goes through env, never through the shell) env: ISSUE_NUMBER: ${{ github.event.issue.number }} ISSUE_TITLE: ${{ github.event.issue.title }} ISSUE_BODY: ${{ github.event.issue.body }} BASE_REF: ${{ github.event.repository.default_branch }} run: | mkdir -p .agent { cat .github/agent/implement.md printf '\nBase ref for scripts/agent-gates.sh: origin/%s\n' "$BASE_REF" printf '\n<issue number="%s">\n# %s\n\n%s\n</issue>\n' \ "$ISSUE_NUMBER" "$ISSUE_TITLE" "$ISSUE_BODY" } > .agent/task.md
# AGENT STEP - name: Run Claude Code uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ github.token }} # read-only here; without it the action mints a Claude App token that can push prompt: "Read .agent/task.md and follow it exactly." claude_args: >- --max-turns 60 --max-budget-usd 10 --allowedTools "Read,Edit,Write,Grep,Glob,Bash(npm run *),Bash(npm test *),Bash(scripts/agent-gates.sh *),Bash(git diff *),Bash(git status *)"
- name: Hand the change to the next job as a patch run: | git add -A git diff --cached --binary > agent.patch - uses: actions/upload-artifact@v7 with: name: agent-output path: | agent.patch .agent/result.json include-hidden-files: true if-no-files-found: warn
open-pr: needs: agent runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/create-github-app-token@v3 id: app with: client-id: ${{ vars.AGENT_APP_CLIENT_ID }} private-key: ${{ secrets.AGENT_APP_PRIVATE_KEY }} - uses: actions/checkout@v7 with: token: ${{ steps.app.outputs.token }} - uses: actions/download-artifact@v8 with: { name: agent-output, path: /tmp/agent }
- name: Commit, push and open a draft pull request env: GH_TOKEN: ${{ steps.app.outputs.token }} ISSUE: ${{ github.event.issue.number }} TITLE: ${{ github.event.issue.title }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | if [ ! -s /tmp/agent/agent.patch ]; then gh issue comment "$ISSUE" --body "The agent produced no change. Run: $RUN_URL" gh issue edit "$ISSUE" --add-label agent:needs-human --remove-label agent:ready exit 1 fi branch="agent/issue-$ISSUE" git switch -c "$branch" git apply --index /tmp/agent/agent.patch git -c user.name="agent-pipeline" -c user.email="agent-pipeline@users.noreply.github.com" \ commit -m "Implement #$ISSUE: $TITLE" -m "Agent-Run: $RUN_URL" git push -u origin "$branch" { echo "Closes #$ISSUE" echo echo "### Agent's own report (a claim, not evidence)" jq -r '"Status: \(.status). \(.stop_reason)", (.items[] | "- \(.claim) -> `\(.check)`: \(.result)")' \ /tmp/agent/.agent/result.json 2>/dev/null || echo "No self-report was written." echo echo "CI posts the evidence bundle as a comment. Run: $RUN_URL" } > body.md gh pr create --draft --head "$branch" --title "$TITLE (#$ISSUE)" \ --body-file body.md --label agent-pr gh issue edit "$ISSUE" --remove-label agent:ready
report-failure: needs: agent if: always() && (needs.agent.result == 'failure' || needs.agent.result == 'cancelled') runs-on: ubuntu-latest permissions: issues: write steps: - name: Hand the issue to a human instead of stalling silently env: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} ISSUE: ${{ github.event.issue.number }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | gh issue comment "$ISSUE" --body "The agent run did not finish (budget, timeout or an action error). Run: $RUN_URL" gh issue edit "$ISSUE" --add-label agent:needs-human --remove-label agent:readyUsunięcie agent:ready na końcu sprawia, że kolejne zdarzenie z etykietą nie uruchomi duplikatu na tym samym zgłoszeniu. Żeby powtórzyć przebieg, usuń gałąź i nadaj etykietę ponownie. Job report-failure obsługuje drugie wyjście: gdy job agenta się wyłoży albo przekroczy limit czasu, open-pr w ogóle nie rusza, więc bez niego zgłoszenie zostałoby z agent:ready i bez żadnego śladu. Używa domyślnego GITHUB_TOKEN, bo komentarz i zmiana etykiety na zgłoszeniu nie muszą uruchamiać żadnego workflow.
Workflow powyżej to wersja dla Claude Code. Akcja działa w trybie automatyzacji, bo ma wejście prompt, więc nie czeka na wzmiankę @claude. Zanim Claude wystartuje, akcja sprawdza, czy użytkownik, który wywołał zdarzenie, ma prawo zapisu i nie jest botem. Etykieta nadana przez kogoś z uprawnieniem tylko do triage kończy więc przebieg błędem.
--max-budget-usd figuruje w claude --help (2.1.283) i działa w trybie print, a właśnie tak akcja uruchamia Claude’a. --max-turns jest akceptowana, ale nie ma jej już w --help (sprawdzone 2026-09-26), więc jako twarde limity traktuj budżet i timeout-minutes joba.
Lista dozwolonych narzędzi nie daje Claude’owi git commit, git push ani narzędzi GitHuba, a job nie daje akcji niczego, czym mogłaby wypchnąć zmiany: github_token: ${{ github.token }} przy contents: read służy tylko do odczytu, a bez id-token: write akcja nie wymieni tokenu OIDC na token aplikacji Claude GitHub. Nie zmieniaj tego; jeśli usuniesz github_token, akcja wróci do tokenu aplikacji, który może zapisywać zawartość. Jeden wpis na liście zasługuje na drugie spojrzenie: Bash(npm run *) uruchamia to, co mówi package.json, łącznie ze skryptem, który agent właśnie dopisał. Dlatego package.json jest na liście chronionych ścieżek i w CODEOWNERS.
Jeśli wolisz rozmowę zamiast potoku, tryb tagowania akcji też reaguje na etykietę: wejście label_trigger wskazuje etykietę, która go uruchamia. W tym trybie Claude wypycha gałąź claude/ i odpowiada linkiem do wstępnie wypełnionej strony tworzenia pull requesta, zamiast samemu go otworzyć — tak mówi FAQ akcji (sprawdzone 2026-09-26). To zostawia w ścieżce kliknięcie człowieka, czyli odwrotność tego, co buduje ta strona.
Routines w Claude Code nie zastąpią tego workflow: ich wyzwalacze GitHub przyjmują tylko zdarzenia pull requestów i wydań, a nie zgłoszeń (dokumentacja routines, sprawdzone 2026-09-26). Routine pasuje za to do strony review; zobacz routines w Claude Code.
Zastąp AGENT STEP akcją Codeksa. Uprawnienia joba zostają przy contents: read:
# AGENT STEP - name: Run Codex uses: openai/codex-action@v1 with: openai-api-key: ${{ secrets.OPENAI_API_KEY }} permission-profile: ":workspace" prompt-file: .agent/task.md output-schema-file: .github/agent/result.schema.json output-file: .agent/result.json:workspace to profil uprawnień, który README akcji zaleca dla workflow edytujących checkout. Nie daje dostępu do sieci, dlatego npm ci działa przed tym krokiem. Nie da się go łączyć z wejściem sandbox: jeśli ustawisz oba, akcja zakończy się błędem, zanim Codex wystartuje. Domyślne safety-strategy: drop-sudo odbiera użytkownikowi runnera sudo przed uruchomieniem Codeksa, a README radzi, żeby akcja była ostatnim uprzywilejowanym krokiem w jobie. To kolejny powód, dla którego commit powstaje w osobnym jobie.
output-schema-file przekazuje --output-schema do codex exec, więc końcowa wiadomość musi pasować do schematu i trafia do .agent/result.json. Codex używa domyślnego modelu, dopóki nie ustawisz wejścia model.
Dwie alternatywy uruchamiają Codeksa bez workflow. W Linearze możesz przypisać zgłoszenie do Codeksa albo wspomnieć @Codex (sprawdzone 2026-08-28; zobacz Codex w Slacku i Linearze), a codex cloud exec --env ENV_ID "…" wysyła zadanie do Codex cloud z terminala (eksperymentalne w CLI 0.157.1). Obie działają w Codex cloud, więc bramki i dowody i tak muszą się wykonać w twoim CI, kiedy pull request już istnieje. Opcje samej akcji opisuje akcja GitHub dla Codeksa.
Cursor uruchamia agenta w Cloud Agencie i sam otwiera pull request, więc wersja dla Cursora zastępuje oba joby jednym. Zapisz ten skrypt jako .github/agent/cursor-run.mjs. Używa @cursor/sdk 1.0.32, którego definicje typów sprawdziliśmy 2026-09-26; SDK wymaga Node 22.13 lub nowszego.
import { readFileSync, appendFileSync } from 'node:fs';import { Agent } from '@cursor/sdk';
const agent = await Agent.create({ apiKey: process.env.CURSOR_API_KEY, cloud: { repos: [{ url: `https://github.com/${process.env.GITHUB_REPOSITORY}`, startingRef: process.env.BASE_REF }], autoCreatePR: true, metadata: { issue: process.env.ISSUE_NUMBER }, },});const run = await agent.send(readFileSync('.agent/task.md', 'utf8'));const result = await run.wait();const prUrl = result.git?.branches?.[0]?.prUrl ?? '';appendFileSync(process.env.GITHUB_OUTPUT, `agent_id=${agent.agentId}\npr_url=${prUrl}\n`);if (result.status !== 'finished' || !prUrl) process.exit(1);Następnie użyj tego joba zamiast agent i open-pr:
agent-cursor: if: github.event.label.name == 'agent:ready' runs-on: ubuntu-latest timeout-minutes: 60 permissions: { contents: read } steps: - uses: actions/checkout@v7 with: { persist-credentials: false } - uses: actions/setup-node@v7 with: { node-version: 22 } # Reuse the "Build the task file" step from the Claude Code workflow here. - run: npm install --no-save @cursor/sdk@1.0.32 - id: cursor env: CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }} BASE_REF: ${{ github.event.repository.default_branch }} ISSUE_NUMBER: ${{ github.event.issue.number }} run: node .github/agent/cursor-run.mjs - uses: actions/create-github-app-token@v3 id: app with: client-id: ${{ vars.AGENT_APP_CLIENT_ID }} private-key: ${{ secrets.AGENT_APP_PRIVATE_KEY }} - name: Hand the pull request to the loop env: GH_TOKEN: ${{ steps.app.outputs.token }} PR_URL: ${{ steps.cursor.outputs.pr_url }} AGENT_ID: ${{ steps.cursor.outputs.agent_id }} run: | gh pr comment "$PR_URL" --body "cursor-agent-id: $AGENT_ID" gh pr edit "$PR_URL" --add-label agent-prEtykietę nadaje token aplikacji z tego samego powodu co push w pozostałych zakładkach: etykieta dodana przez GITHUB_TOKEN nie uruchomiłaby workflow pętli. Komentarz z identyfikatorem agenta pozwala pętli poprawek wznowić ten sam Cloud Agent.
W przeciwieństwie do obu akcji SDK nie daje listy dozwolonych narzędzi dla agentów w chmurze (opcja tools w wersji 1.0.32 działa tylko lokalnie), więc ograniczeniem są maszyna wirtualna Cloud Agenta i twoje bramki. Automations w Cursorze, które mogą uruchomić Cloud Agent wyzwalaczem Source control (sprawdzone 2026-08-28), to alternatywa bez kodu; wersja z SDK trzyma wyzwalacz, etykietę i limity w repozytorium, gdzie przechodzą review. Zobacz Cloud Agents i Automations w Cursorze.
Uruchom bramki i opublikuj pakiet dowodów
Dział zatytułowany „Uruchom bramki i opublikuj pakiet dowodów”Workflow pętli obsługuje każdy pull request z etykietą agent-pr. Uruchamia bramki od zera i publikuje wynik jako pakiet dowodów. Deklaracja agenta, że bramki przeszły, nigdy nie jest dowodem.
Praca jest podzielona na dwa joby, bo kod pull requesta to wynik pracy agenta i trzeba go traktować jako niezaufany:
gates(zgłaszany jako sprawdzenieevidence) pobiera głowę pull requesta dopr/zpersist-credentials: false, pobierascripts/i.github/agent/z gałęzi bazowej dotrusted/i uruchamiatrusted/scripts/agent-gates.shwewnątrzpr/. Ma tylko uprawnieniecontents: readi nie odwołuje się do żadnego sekretu, więc skrypty cyklu życianpm cii testy napisane przez agenta nie mają czego ukraść ani żadnego późniejszego uprzywilejowanego kroku do przejęcia.publishrusza po nim i nigdy nie pobiera ani nie uruchamia kodu pull requesta. Ściąga log bramek, buduje pakiet skryptemevidence.shz gałęzi bazowej na podstawie API GitHuba, komentuje przezGITHUB_TOKENi dopiero wtedy tworzy token aplikacji dlagh pr ready.
Sam log bramek powstał w jobie, który uruchamiał kod agenta, więc pakiet traktuje go jak log: wynik „pass” albo „fail” pochodzi z rezultatu joba gates, a nie z pliku.
#!/usr/bin/env bash# scripts/evidence.sh <pr-number> <gates-result> <gates-md>: prints the evidence bundle as Markdown.# Reads the pull request through the GitHub API only; it never checks out or runs the PR's code.set -euo pipefailPR="$1"; RESULT="$2"; GATES="$3"pr=$(gh pr view "$PR" --json headRefOid,additions,deletions,changedFiles,files,closingIssuesReferences)issues=$(jq -r '[.closingIssuesReferences[].number | "#\(.)"] | join(", ")' <<<"$pr")attempts=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR/commits" --paginate \ --jq '.[] | select(.commit.message | test("(^|\n)Agent-Fix:")) | .sha' | wc -l | tr -d ' ')sensitive=$(jq -r '.files[].path | select(test("^(src/auth/|src/billing/|migrations/)"))' <<<"$pr")cat <<EOF## Evidence bundle**Closes:** ${issues:-none} · **Head:** \`$(jq -r '.headRefOid[0:7]' <<<"$pr")\` · **Gates:** $RESULT · **Fix attempts:** $attempts of 3
### Gate log (written by the job that ran the PR's code; the check result above is what counts)$(cat "$GATES" 2>/dev/null || echo "No gate log was uploaded.")
### Size$(jq -r '"\(.changedFiles) files, +\(.additions) -\(.deletions)"' <<<"$pr")
### Sensitive paths (a named human reads these)${sensitive:-none}EOFTo minimalny pakiet. Pełny kontrakt, ze zmianą specyfikacji, wynikami akceptacji dla każdego kryterium, zrzutami ekranu i pochodzeniem zmiany, opisuje pakiet dowodów; rozbuduj ten skrypt w jego stronę, kiedy potok już działa.
name: agent-loopon: pull_request: types: [opened, synchronize, reopened, labeled] pull_request_review: types: [submitted]
concurrency: group: agent-loop-${{ github.event.pull_request.number }} cancel-in-progress: false
jobs: gates: name: evidence # the required check: PR code runs here, with no secrets and no write token if: >- contains(github.event.pull_request.labels.*.name, 'agent-pr') && (github.event.action != 'labeled' || github.event.label.name == 'agent-pr') runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: read steps: - name: Check out the PR's code (untrusted, it is the agent's output) uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.head.sha }} path: pr fetch-depth: 0 persist-credentials: false - name: Check out the gate rules from the base branch (trusted) uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.base.ref }} path: trusted persist-credentials: false sparse-checkout: | scripts .github/agent - uses: actions/setup-node@v7 with: { node-version: 22, cache: npm, cache-dependency-path: pr/package-lock.json } - name: Run the base branch's gates against the PR's code working-directory: pr env: BASE: origin/${{ github.event.pull_request.base.ref }} run: | npm ci ../trusted/scripts/agent-gates.sh "$BASE" - uses: actions/upload-artifact@v7 if: always() with: name: gate-logs path: pr/.agent/ include-hidden-files: true if-no-files-found: warn
publish: needs: gates if: always() && (needs.gates.result == 'success' || needs.gates.result == 'failure') runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - name: Check out trusted scripts only (the PR's code is never checked out here) uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.base.ref }} persist-credentials: false sparse-checkout: scripts - uses: actions/download-artifact@v8 continue-on-error: true with: { name: gate-logs, path: /tmp/gates } - name: Post the evidence bundle env: GH_TOKEN: ${{ github.token }} PR: ${{ github.event.pull_request.number }} RESULT: ${{ needs.gates.result }} run: | scripts/evidence.sh "$PR" "$RESULT" /tmp/gates/gates.md > evidence.md gh pr comment "$PR" --body-file evidence.md --edit-last --create-if-none - uses: actions/create-github-app-token@v3 id: app if: needs.gates.result == 'success' && github.event.pull_request.draft with: client-id: ${{ vars.AGENT_APP_CLIENT_ID }} private-key: ${{ secrets.AGENT_APP_PRIVATE_KEY }} - name: Mark ready for review (App token, so review workflows start) if: needs.gates.result == 'success' && github.event.pull_request.draft env: GH_TOKEN: ${{ steps.app.outputs.token }} run: gh pr ready "${{ github.event.pull_request.number }}"Job gates zgłasza swoje sprawdzenie pod nazwą evidence; tę nazwę wpisz do wymaganych sprawdzeń w rulesecie. Oznaczenie pull requesta jako gotowego tokenem aplikacji ma znaczenie: boty do review, które startują na ready_for_review, na przykład workflow review Claude Code, pomijają szkice i nigdy nie zobaczą zdarzenia utworzonego przez GITHUB_TOKEN.
Zamknij pętlę jobem review i poprawek z limitem
Dział zatytułowany „Zamknij pętlę jobem review i poprawek z limitem”Joby poprawek uruchamiają się, gdy bramki nie przejdą albo człowiek zgłosi review ze statusem „changes requested”. Liczą wcześniejsze stopki Agent-Fix: w commitach na gałęzi, więc licznik prób przetrwa ponowne uruchomienia i nie potrzebuje zapisanego stanu. Przy czwartej próbie workflow nadaje pull requestowi etykietę agent:needs-human i się zatrzymuje.
Obowiązuje ten sam podział zaufania. fix-plan nigdy nie pobiera kodu pull requesta: liczy próby przez API, zatrzymuje pętlę, gdy pull request dotyka chronionej ścieżki, i buduje zadanie poprawki z fix.md z gałęzi bazowej. Tylko fix uruchamia agenta na kodzie pull requesta, z tokenem tylko do odczytu, a token aplikacji ma wyłącznie push-fix, który niczego z gałęzi nie uruchamia. Ponieważ fix-plan zatrzymuje się przy każdej zmianie package.json albo lockfile’a, npm ci w fix instaluje dokładnie to, co zainstalowałaby gałąź bazowa. Tę samą maszynę stanów, z tabelą uprawnień, opisuje ograniczona pętla review i poprawek w PR.
Dodaj te joby do agent-loop.yml:
fix-plan: needs: gates if: >- always() && contains(github.event.pull_request.labels.*.name, 'agent-pr') && !contains(github.event.pull_request.labels.*.name, 'agent:needs-human') && (needs.gates.result == 'failure' || github.event.review.state == 'changes_requested') runs-on: ubuntu-latest permissions: contents: read pull-requests: write outputs: attempt: ${{ steps.plan.outputs.attempt }} steps: - name: Check out the fix prompt and the protected-path list from the base branch uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.base.ref }} persist-credentials: false sparse-checkout: .github/agent - id: plan env: GH_TOKEN: ${{ github.token }} PR: ${{ github.event.pull_request.number }} run: | n=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR/commits" --paginate \ --jq '.[] | select(.commit.message | test("(^|\n)Agent-Fix:")) | .sha' | wc -l | tr -d ' ') touched=$(gh pr view "$PR" --json files --jq '.files[].path' \ | grep -E -f .github/agent/protected-paths.txt || true) if [ -n "$touched" ]; then gh pr edit "$PR" --add-label agent:needs-human gh pr comment "$PR" --body "Protected paths changed: $(echo $touched). The loop stops here; a human decides." echo "attempt=stop" >> "$GITHUB_OUTPUT" elif [ "$n" -ge 3 ]; then gh pr edit "$PR" --add-label agent:needs-human gh pr comment "$PR" --body "Fix limit reached (3 attempts). Handing over to the issue owner." echo "attempt=stop" >> "$GITHUB_OUTPUT" else echo "attempt=$((n + 1))" >> "$GITHUB_OUTPUT" fi - uses: actions/download-artifact@v8 if: steps.plan.outputs.attempt != 'stop' && needs.gates.result == 'failure' with: { name: gate-logs, path: /tmp/gates } - name: Build the fix task (feedback is data) if: steps.plan.outputs.attempt != 'stop' env: GH_TOKEN: ${{ github.token }} PR: ${{ github.event.pull_request.number }} BASE_REF: ${{ github.event.pull_request.base.ref }} run: | mkdir -p task { cat .github/agent/fix.md printf '\nBase ref for scripts/agent-gates.sh: origin/%s\n' "$BASE_REF" echo '<feedback>' cat /tmp/gates/gates.md 2>/dev/null for f in /tmp/gates/*.log; do [ -f "$f" ] && { echo "## $f"; tail -n 80 "$f"; }; done gh pr view "$PR" --json reviews \ --jq '.reviews[] | select(.state == "CHANGES_REQUESTED") | "- review by \(.author.login): \(.body)"' gh api "repos/$GITHUB_REPOSITORY/pulls/$PR/comments" \ --jq '.[] | "- \(.path):\(.line // .original_line) \(.user.login): \(.body)"' echo '</feedback>' } > task/task.md - uses: actions/upload-artifact@v7 if: steps.plan.outputs.attempt != 'stop' with: { name: fix-task, path: task/task.md }
fix: needs: fix-plan if: always() && needs.fix-plan.result == 'success' && needs.fix-plan.outputs.attempt != 'stop' runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: read # the agent runs PR code here, so the job holds no write token steps: - uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.head.ref }} fetch-depth: 0 persist-credentials: false - uses: actions/download-artifact@v8 with: { name: fix-task, path: .agent } - uses: actions/setup-node@v7 with: { node-version: 22, cache: npm } - run: npm ci # safe to run only because fix-plan stops on any change to package.json or the lockfile
# AGENT STEP: the same step as in agent-issue.yml
- run: git add -A && git diff --cached --binary > agent.patch - uses: actions/upload-artifact@v7 with: { name: fix-output, path: agent.patch }
push-fix: needs: [fix-plan, fix] if: always() && needs.fix.result == 'success' runs-on: ubuntu-latest permissions: { contents: read } steps: - uses: actions/create-github-app-token@v3 id: app with: client-id: ${{ vars.AGENT_APP_CLIENT_ID }} private-key: ${{ secrets.AGENT_APP_PRIVATE_KEY }} - uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ steps.app.outputs.token }} - uses: actions/download-artifact@v8 with: { name: fix-output, path: /tmp/fix } - env: GH_TOKEN: ${{ steps.app.outputs.token }} PR: ${{ github.event.pull_request.number }} N: ${{ needs.fix-plan.outputs.attempt }} run: | if [ ! -s /tmp/fix/agent.patch ]; then gh pr edit "$PR" --add-label agent:needs-human gh pr comment "$PR" --body "Fix attempt $N produced no change. Handing over." exit 0 fi git apply --index /tmp/fix/agent.patch git -c user.name="agent-pipeline" -c user.email="agent-pipeline@users.noreply.github.com" \ commit -m "Address review and gate feedback" -m "Agent-Fix: $N" git pushPush uruchamia nowy przebieg synchronize, który ponownie wykonuje bramki, więc każda próba jest oceniana dokładnie tak samo jak pierwsza.
W jobie poprawek użyj tego samego kroku Run Claude Code. Pętla potrzebuje oprócz bramek także recenzenta: workflow z pluginem code-review z dokumentacji Claude Code GitHub Actions publikuje komentarze w kodzie, gdy pull request zostaje otwarty, zaktualizowany lub oznaczony jako gotowy, i pomija szkice. Te komentarze trafiają do promptu poprawek przez wywołanie pulls/…/comments powyżej. Zarządzane Code Review to opcja bez workflow (research preview, Team i Enterprise); jego check run „always completes with a neutral conclusion”, więc informuje pętlę, ale nigdy jej nie blokuje.
Sesje w chmurze mają też auto-fix (/autofix-pr z terminala na gałęzi pull requesta): Claude obserwuje pull request, wypycha poprawkę, gdy jest oczywista, i pyta cię, gdy komentarz jest niejednoznaczny. Jego dokumentacja nie opisuje limitu prób (sprawdzone 2026-09-26) i zaznacza, że auto-fix nie reaguje na konflikty merge’a, więc pętlą obowiązującą zostaje job z limitem powyżej.
W jobie poprawek użyj tego samego kroku Run Codex. Po stronie review @codex review w pull requeście zleca review Codeksa na GitHubie, a własne reguły review żyją w AGENTS.md (sprawdzone 2026-08-28). Wpisz do tych reguł klasy z CODEOWNERS, żeby każde review nazywało klasę zmiany, którą znalazło.
Ponieważ :workspace nie ma sieci, job poprawek pobiera komentarze z review przed krokiem agenta i umieszcza je w .agent/task.md. Codex nigdy nie potrzebuje tokenu GitHuba do obsługi pętli.
Krok poprawek w Cursorze wznawia Cloud Agent, który otworzył pull request, więc zachowuje kontekst pierwszego przebiegu. Zapisz go jako .github/agent/cursor-fix.mjs:
import { readFileSync } from 'node:fs';import { Agent } from '@cursor/sdk';
const agent = await Agent.resume(process.env.AGENT_ID, { apiKey: process.env.CURSOR_API_KEY });const run = await agent.send(readFileSync('.agent/task.md', 'utf8'));const result = await run.wait();if (result.status !== 'finished') process.exit(1);W fix-plan odczytaj AGENT_ID z komentarza cursor-agent-id: poleceniem gh pr view "$PR" --json comments i przekaż go dalej jako wyjście joba. W fix uruchom skrypt zamiast kroku agenta; potrzebuje artefaktu fix-task, a nie kodu pull requesta. Usuń job push-fix: Cloud Agent wypycha zmiany na własną gałąź. Zapisz SHA głowy pull requesta poleceniem gh pr view "$PR" --json headRefOid przed przebiegiem i po nim, a jeśli się nie zmieniło, niech kolejny job z pull-requests: write nada etykietę agent:needs-human. To sprawdzenie trzyma pętlę w ryzach, nawet gdy kolejny przebieg skończy się bez commita. Próby licz etykietą nadawaną przy każdym przebiegu zamiast stopki Agent-Fix:, bo komunikat commita należy do Cursora.
Po stronie review Bugbot „reviews pull requests and identifies bugs, security issues, and code quality problems” (sprawdzone 2026-08-28); jego komentarze trafiają do promptu poprawek tą samą drogą. Zobacz Bugbot.
Skąd wiesz, że potok produkuje kod nadający się do merge’a?
Dział zatytułowany „Skąd wiesz, że potok produkuje kod nadający się do merge’a?”Bramki dowodzą poprawności każdego pull requesta. Poniższe pięć liczb dowodzi, że działa cały potok, i pochodzi z etykiet oraz stopek commitów, które już masz. Przez pierwszy miesiąc tech lead przegląda je co tydzień.
| Miara | Definicja | Dobry kierunek |
|---|---|---|
| Odsetek bramek zaliczonych za pierwszym razem | Pull requesty agenta, których pierwszy przebieg evidence przeszedł, podzielone przez wszystkie pull requesty agenta | Rośnie; niski odsetek oznacza, że poprawić trzeba zgłoszenia albo prompt, a nie model |
| Próby poprawek na zmergowany PR | Liczba stopek Agent-Fix: w zmergowanych pull requestach agenta | Najczęściej 0 lub 1 |
| Odsetek przekazań | Pull requesty, które dostały agent:needs-human, podzielone przez wszystkie pull requesty agenta | Spada, ale nigdy do zera; zero oznacza zbyt luźne warunki stopu |
| Odsetek ręcznych poprawek | Pull requesty agenta, do których człowiek wypchnął commit przed merge’em | Spada |
| Odsetek revertów po merge’u | Pull requesty agenta cofnięte albo poprawiane w ciągu 14 dni | Na poziomie twojej bazy dla kodu pisanego przez ludzi lub niżej |
Zatwierdzenie zostaje przy ludziach. Właściciel zgłoszenia zatwierdza merge po przeczytaniu pakietu dowodów, a nie diffa; czytanie dowodów zamiast kodu pokazuje, co czytać i w jakiej kolejności. CODEOWNERS wymusza wskazanego czytelnika dla uwierzytelniania, pieniędzy, schematów, migracji i samej wyroczni. Tech lead odpowiada za limity (trzy próby poprawek, limit kwoty, limity czasu jobów) i za to, które klasy zgłoszeń mogą dostać agent:ready.
Zacznij od dziesięciu zgłoszeń oznaczanych przez jedną osobę. Porównaj czas od etykiety do merge’a każdego pull requesta i odsetek przekazań człowiekowi z tym, ile czasu zespół szacował na te dziesięć zgłoszeń, a dopiero potem udostępnij etykietę zespołowi. To, jak takie review mieszczą się w dniu zespołu, opisuje zarządzanie kolejką review.
Co się psuje w potoku od zgłoszenia do PR?
Dział zatytułowany „Co się psuje w potoku od zgłoszenia do PR?”Pull request się otwiera, ale CI na nim nie rusza. Push albo pull request powstał z użyciem GITHUB_TOKEN, a GitHub nie uruchamia workflow ze zdarzeń tego tokenu. Naprawa: wypychaj, nadawaj etykiety i oznaczaj gotowość tokenem aplikacji GitHub, jak robi każdy workflow na tej stronie, a potem raz zamknij i otwórz pull request, żeby uruchomić CI.
Przebieg od razu kończy się błędem uprawnień. Osoba, która nadała agent:ready, ma uprawnienia do triage, ale nie do zapisu, a obie akcje sprawdzają użytkownika wywołującego, zanim agent wystartuje. Naprawa: ogranicz, kto może nadawać etykietę, albo nadaj ją ponownie jako maintainer. Nie obchodź tego przez allowed_non_write_users ani allow-users; to otwiera przebieg dla każdego, kto może założyć zgłoszenie.
Codex nie może zainstalować pakietu ani połączyć się z usługą. Profil :workspace nie ma dostępu do sieci. Naprawa: instaluj wszystkie zależności przed krokiem agenta, testy integracyjne wymagające usług uruchamiaj w jobie evidence, a przebieg bramek agenta ogranicz do tego, co działa offline.
Bramki przechodzą, bo agent zmienił test. Pętla nagradzana za zielony wynik znajdzie najtańszą drogę do zielonego. Naprawa: CI uruchamia agent-gates.sh z gałęzi bazowej, więc agent nie przepisze reguł, według których jest oceniany, a strażnik wyroczni odrzuca każdą zmianę w chronionej ścieżce, w tym w skryptach bramek, package.json i konfiguracji testów oraz lintera. Dopisz do protected-paths.txt i do CODEOWNERS każdy katalog, którego testy definiują zachowanie.
Pętla odbija się między dwoma błędami. Próba 2 cofa próbę 1, a próba 3 ją powtarza. Naprawa: limit zatrzymuje to po trzech próbach, a prompt poprawek każe agentowi przestać, gdy bramka pada drugi raz z tego samego powodu. Jeśli zdarza się to często, zgłoszenie nie było gotowe dla agenta; odeślij je do kształtowania backlogu.
Zgłoszenie albo komentarz z review zawiera instrukcje. Każdy, kto może edytować zgłoszenie, może dopisać „zaktualizuj też klucz wdrożeniowy”. Naprawa: workflow przekazuje tekst zgłoszenia przez zmienne środowiskowe, owija go w znaczniki, które prompt uznaje za dane, i nie daje agentowi tokenu z prawem zapisu ani narzędzi GitHuba, a w Codeksie także sieci. Joby CI, które uruchamiają kod pull requesta, nie mają sekretów. Szerszy model opisuje model zagrożeń dla agentów.
Zgłoszenie ma agent:ready i nic się nie dzieje. Job agenta wyczerpał budżet, przekroczył limit czasu albo wyłożył się wewnątrz akcji, więc open-pr został pominięty. Naprawa: job report-failure komentuje zgłoszenie linkiem do przebiegu i zamienia agent:ready na agent:needs-human. Przeczytaj log przebiegu, zanim nadasz etykietę ponownie; zatrzymanie na budżecie zwykle znaczy, że zgłoszenie było za duże.
Człowiek wypchnął zmiany na gałąź, a kolejna poprawka nadpisała jego zamiar. Pętla nie wie, kto steruje. Naprawa: gdy człowiek przejmuje pull request, nadaje agent:needs-human, a każdy job poprawek sprawdza tę etykietę przed startem.
Gałąź bazowa się przesunęła i pull request ma teraz konflikt. Żadna bramka nie pada, więc żadna poprawka nie startuje. Naprawa: dodaj zaplanowany job, który robi rebase otwartych gałęzi agent-pr, albo pozwól właścicielowi rozwiązać konflikty ręcznie; potok służy do pierwszej wersji, a nie do ostatniego etapu na ruchliwej gałęzi.
Wydatki powoli rosną. Niejasne zgłoszenie trzykrotnie zużywa cały budżet tur i kwoty. Naprawa: odsetek bramek zaliczonych za pierwszym razem pokaże to wcześniej niż faktura. Obniż --max-budget-usd dla joba poprawek i przyjrzyj się zgłoszeniom z największą liczbą prób, a nie modelowi.