Przejdź do głównej zawartości

Równoległa praca agentów w zespole na poziomie 3 — izoluj zadania i ogranicz kolejkę

Równoległa praca zespołu na poziomie 3 drabiny autonomii oznacza, że kilka zadań agentów działa jednocześnie bez współdzielenia plików, portów ani danych, pod limitem współbieżności ustalonym dla każdej klasy zadań na podstawie zmierzonej przepustowości review i CI, z imiennym właścicielem każdego merge. Równoległość opłaca się tylko wtedy, gdy zaakceptowany throughput przewyższa szeregowy baseline, a rework nie rośnie.

Twoi inżynierowie już teraz uruchamiają po dwa, trzy agenty. Pull requesty przychodzą szybciej niż kiedykolwiek, a release nie jest ani o krok bliżej: dwa branche przepisały ten sam moduł cennika, testy padły, bo dev server innego agenta zajął port, i nikt nie wie, kto ma rozwiązać konflikt. To jest fan-out bez workflow zespołu, a ta strona jest dla tech leada albo CTO, który musi zamienić go w workflow.

Mechanikę pracy jednego developera na worktrees opisuje Uruchamiaj równoległe agenty w izolowanych worktrees. Arytmetykę po stronie review — Kolejka code review, gdy pull requesty otwierają agenci. Ta strona obejmuje standard zespołu pomiędzy nimi: która praca może iść równolegle, jak izolować stan, kto integruje i jak udowodnić, że to się opłaca.

Co daje zespołowi standard pracy równoległej na poziomie 3

Dział zatytułowany „Co daje zespołowi standard pracy równoległej na poziomie 3”
  • Tabelę decyzyjną: które klasy zadań idą równolegle, a które szeregowo.
  • Szablon karty zadania, którą każde równoległe zadanie ma, zanim agent ruszy.
  • Skrypt izolacji, który odmawia startu, gdy klasa zadania jest pełna, a w przeciwnym razie daje każdemu zadaniu własny worktree, branch, porty i nazwę bazy danych.
  • Limit współbieżności per klasa zadań i regułę jego zmiany.
  • Sprawdzenie nakładania się zmian przed merge oraz właściciela integracji, który na nie reaguje.
  • Pięć definicji metryk i dwutygodniowy eksperyment porównujący pracę równoległą z szeregowym baseline.
  • Jednostronicową politykę zespołu, którą możesz dziś zacommitować do repozytorium.

Jak CTO Scorecard ocenia równoległą pracę zespołu

Dział zatytułowany „Jak CTO Scorecard ocenia równoległą pracę zespołu”

Pytanie 14 w CTO Scorecard brzmi: czy zespół uruchamia równoległe zadania agentów w izolowanych worktrees. Cztery odpowiedzi odpowiadają dowodom poniżej. Pełną liczbę punktów daje tylko ostatnia, a każdy jej element musi być czymś, co możesz pokazać, a nie czymś, co zamierzasz.

OdpowiedźPunktyCo możesz zaobserwowaćNastępny krok
Nikt0Jedna sesja agenta na inżyniera, jeden checkoutPilotaż jednej klasy zadań z dwoma izolowanymi zadaniami
Eksperymentują pojedynczy devs1Prywatne nawyki z worktrees, brak wspólnych konwencjiUstandaryzuj skrypt izolacji i prefiks branchy
Polecane / dofinansowane dla seniorów2Narzędzia są opłacone, ale nie ma limitu, właściciela ani pomiaruUstal limity per klasa i wyznacz właścicieli integracji
Workflow zespołu z izolacją stanu, zmierzonym limitem równoległości, właścicielem integracji i limitem kolejki review3Plik polityki, bramka dispatchu, sprawdzenie nakładania się, cotygodniowe metrykiTrzymaj limit tam, gdzie zaakceptowany throughput jest najwyższy, a nie tam, gdzie liczba PR osiąga szczyt

Dlaczego więcej agentów może obniżyć zaakceptowany throughput

Dział zatytułowany „Dlaczego więcej agentów może obniżyć zaakceptowany throughput”

Generowanie skaluje się z liczbą agentów; review i integracja nie. Raport Faros AI AI Engineering Report 2026: The Acceleration Whiplash (kwiecień 2026; telemetria dostawcy z 22 000 developerów na jego własnej platformie) zmierzył obie strony w tym samym okresie: throughput zadań na developera wzrósł o 33,7%, mediana czasu w review o 441,5%, a liczba incydentów na pull request o 242,7%.

To jest sufit poziomu 3: tempo wyznacza kolejka, a nie agent. Jednostką, którą zarządzasz, jest więc praca zaakceptowana — zmergowana, zielona i niecofnięta — a nie liczba otwartych pull requestów czy działających agentów.

Równoległość jest cechą pracy, nie narzędzia. Dwa zadania mogą działać jednocześnie tylko wtedy, gdy żadne nie zmienia czegoś, co czyta drugie. Klasyfikuj każde zadanie według tej tabeli przed dispatchem i dopisuj własne klasy, gdy się ich nauczysz.

Klasa zadaniaDomyślnieDlaczego
Pionowy wycinek funkcji (vertical-slice) we własnym module lub pakiecieRównoleglePosiadane ścieżki się nie pokrywają; konflikty są rzadkie i tekstowe
Testy lub testy charakteryzujące istniejący kodRównolegleDodaje pliki; wyrocznię przeglądasz raz, a nie per zadanie
Izolowana poprawka błędu z czerwonym (niezaliczonym) testemRównolegleMały diff, jasne kryterium akceptacji
Zmiana współdzielonej abstrakcji, klasy bazowej lub kontraktu APISzeregowoCzyta ją każde równoległe zadanie, więc unieważnia każde z nich
Migracja schematu bazy danychSzeregowoMigracje są uporządkowane; dwie naraz dają konflikt numeracji albo zerwany łańcuch
Aktualizacja zależności lub frameworkaSzeregowoDotyka lockfile’ów i konfiguracji builda, które współdzieli każdy branch
Przekrojowy rename lub przebieg formatowaniaSzeregowo, w pojedynkęKoliduje ze wszystkim; uruchamiaj go, gdy nic innego nie jest w toku

Zadanie szeregowe nie jest zakazane. Działa samo na swoim torze, a zadania równoległe, które od niego zależą, czekają na jego merge.

Napisz kartę zadania dla każdego równoległego zadania

Dział zatytułowany „Napisz kartę zadania dla każdego równoległego zadania”

Agent, który nie zna swoich granic, dryfuje do plików sąsiada. Karta zadania jest kontraktem: backlog gotowy dla agentów kształtuje issue, a karta dodaje to, czego potrzebuje praca równoległa. Przed dispatchem zacommituj kartę do tasks/<slug>.yaml na domyślnym branchu; skrypty czytają ją stamtąd.

tasks/billing-retry.yaml
slug: billing-retry
class: vertical-slice # wiersz z tabeli klas zadań
accepted_plan: docs/plans/billing-retry.md
owned_paths:
- src/billing/retry/**
- tests/billing/retry/**
read_only_paths:
- src/billing/client.ts # współdzielony kontrakt: wolno czytać, nie wolno edytować
depends_on: [] # slugi, które muszą zostać zmergowane wcześniej
checks:
- npm run typecheck
- npm test -- tests/billing/retry
stop_when:
- a change outside owned_paths is needed
- a check fails three times in a row
integration_owner: "@ana"

Lista stop_when jest równie ważna jak zakres. Agent, który trafia na współdzielony kontrakt, ma się zatrzymać i zgłosić, a nie edytować kontrakt i unieważniać trzy inne branche.

Zastąp PLAN_FILE nazwą pliku planu. Przeglądasz graf i tabelę nakładania się, nie kod: jeśli tabela nakładania się nie jest pusta, partia zadań nie jest gotowa.

Git worktree daje każdemu zadaniu własne pliki i branch przy jednej wspólnej bazie obiektów. Nie izoluje portów, baz danych, cache’y, kontenerów ani działającego dev servera. Dwa agenty z osobnymi worktrees i jedną wspólną bazą danych nadal dają dwa zielone przebiegi, które sobie przeczą.

Daj każdemu zadaniu cały zestaw jednym poleceniem, żeby nikt nie składał go ręcznie:

scripts/agent-task.sh
#!/usr/bin/env bash
# Użycie: scripts/agent-task.sh SLUG INDEX
# Odmawia, gdy klasa zadania osiągnęła limit. W przeciwnym razie tworzy jeden
# worktree, jeden branch, jeden blok portów i jeden oznaczony draft PR na zadanie.
set -euo pipefail
slug="$1"
index="$2"
[ "$index" -ge 1 ] || { echo "INDEX must be >= 1; 0 is the main checkout's block"; exit 1; }
# Bramka dispatchu: klasa z karty zadania, limit z tasks/caps.txt.
class=$(yq -r '.class' "tasks/$slug.yaml")
cap=$(awk -v c="$class" '$1 == c { print $2 }' tasks/caps.txt)
[ -n "$cap" ] || { echo "no cap for class $class in tasks/caps.txt"; exit 1; }
open=$(gh pr list --state open --label "class:$class" --limit 200 --json number --jq length)
[ "$open" -lt "$cap" ] || { echo "class $class at cap $cap"; exit 1; }
dir="../$(basename "$PWD")-$slug"
git fetch --quiet origin
git worktree add --quiet -b "agent/$slug" "$dir" origin/main
cat > "$dir/.agent-env" <<ENV
APP_PORT=$((3000 + index * 10))
DEBUG_PORT=$((9229 + index * 10))
DATABASE_NAME=app_${slug//-/_}
ENV
# Zajmij slot: od tej chwili ten draft PR liczy się do limitu.
git -C "$dir" commit --quiet --allow-empty -m "Start agent/$slug"
git -C "$dir" push --quiet -u origin "agent/$slug"
gh pr create --draft --head "agent/$slug" --label "class:$class" \
--title "agent: $slug" --body "Task card: tasks/$slug.yaml"
echo "Worktree $dir on agent/$slug"
cat "$dir/.agent-env"

Limity są w tasks/caps.txt, po jednej klasie i jej limicie w wierszu, na przykład vertical-slice 3 i migration 1, a polityka poniżej wskazuje ten plik, zamiast powtarzać liczby. Raz utwórz etykietę class:<nazwa> dla każdej klasy, na przykład gh label create class:vertical-slice, bo gh pr create --label kończy się błędem dla etykiety, która nie istnieje. Skrypt potrzebuje gh, git i yq v4 (mikefarah/yq; -r działa też z pythonowym yq).

Uruchamiaj go z głównego checkoutu, na przykład scripts/agent-task.sh billing-retry 2. Zaczynaj INDEX od 1: indeks 0 dałby zadaniu domyślne porty głównego checkoutu, czyli dokładnie tę kolizję, której skrypt ma zapobiegać. Cztery szczegóły są celowe. Draft PR otwiera się przy dispatchu, a nie wtedy, gdy agent skończy, więc praca w toku liczy się do limitu i nigdy nie ukrywa się jako branch, którego nikt nie widzi. Prefiks branchy agent/ pozwala każdemu późniejszemu skryptowi znaleźć pracę równoległą. Krok portów równy 10 trzyma narzędzie, które przy zajętym porcie przechodzi na „następny wolny”, w obrębie jego własnego bloku. Dodaj .agent-env do .gitignore i niech skrypty dev go czytają, żeby porty działały bez pamiętania o export. Jeśli domyślny branch nazywa się inaczej, zamień origin/main.

Przy równoczesnym dispatchu bramka jest tylko doradcza: dwie osoby, które uruchomią dispatch w tej samej minucie, obie zobaczą wolny slot, a klasa przekroczy limit. Wyłapią to sprawdzenie nakładania się i przegląd limitów. Jeśli potrzebujesz twardego limitu, uruchamiaj dispatch z jednego workflow w jednej grupie concurrency: w GitHub Actions.

Każde z narzędzi potrafi też samo utworzyć worktree. Izolacja portów i danych nadal jest po twojej stronie we wszystkich trzech.

Sprawdzone z Claude Code 2.1.283 (claude --help):

  • claude --worktree billing-retry (krótko -w) uruchamia sesję w nowym Git worktree. Dodaj --tmux, żeby otworzyć ten worktree we własnej sesji tmux; --tmux wymaga --worktree.
  • claude --bg uruchamia sesję w tle i wypisuje jej ID; claude agents otwiera agent view (research preview), który wyświetla sesje w tle i pokazuje, które z nich czekają na odpowiedź.
  • Wbudowany skill /batch dzieli jedną dużą zmianę na 5–30 jednostek, każdą we własnym worktree. Traktuj każdą jednostkę jak zadanie: nadaj jej posiadane ścieżki tak, jak zrobiłaby to karta, i licz ją do limitu jej klasy.
  • claude rm <id> usuwa sesję w tle wraz z jej worktree, gdy jest to bezpieczne, co załatwia większość sprzątania.

Sprzątaj według harmonogramu, a nie wtedy, gdy zapełni się dysk. git worktree list pokazuje każdy checkout; git worktree remove <dir> odmawia, gdy worktree ma niezacommitowane lub nieśledzone pliki, a git branch -d agent/<slug> odmawia, gdy branch nie jest zmergowany. Obie odmowy to zabezpieczenie, którego chcesz: nigdy nie dodawaj --force do skryptu sprzątającego. Potem uruchom git worktree prune, żeby usunąć wpisy katalogów skasowanych ręcznie. Nadzór nad wieloma sesjami naraz opisuje strona wiele agentów jednocześnie.

Limit per inżynier („po trzy agenty”) ignoruje jedyne ograniczenie, które ma znaczenie: jak szybko zespół potrafi weryfikować i integrować. Wyprowadź limit z kolejki.

  1. Policz limit WIP review. Użyj metody z Kolejki code review: zmierzona przepustowość review pomnożona przez czas w review, który jesteś w stanie zaakceptować. Ta liczba to sufit dla wszystkich otwartych pull requestów agentów łącznie.
  2. Podziel sufit między klasy zadań. Każdej klasie równoległej daj udział, każdej klasie szeregowej limit równy jeden. Zacznij nisko; eksperyment poniżej powie ci, kiedy podnieść limity.
  3. Blokuj dispatch, a nie tworzenie pull requestów. Bramką jest scripts/agent-task.sh powyżej: liczy otwarte pull requesty z etykietą class:<nazwa>, łącznie z draftami, i odmawia, gdy klasa osiągnęła limit z tasks/caps.txt. Blokada przy tworzeniu pull requesta zostawiłaby gotową pracę agenta na branchu, którego nikt nie widzi. Jeśli agenty startują też z workflow albo harmonogramu, najpierw uruchom tam te same linie bramki.
  4. Sprawdzaj osobno pojemność CI. Jeśli mediana przebiegu CI dla partii dłużej czeka w kolejce, niż trwa, to CI jest ograniczeniem i limit spada, dopóki tak nie jest.
  5. Przeglądaj limity co dwa tygodnie. Zmieniają się, gdy zmienia się przepustowość. Obniż limit klasy w tym tygodniu, w którym rośnie jej rework, bez czekania na przegląd.

Konflikty tekstowe to łatwy przypadek — Git je zgłasza. Groźne są konflikty semantyczne: dwa branche, z których każdy przechodzi własne checki, a razem psują build, na przykład jeden zmienia nazwę pola, a drugi dodaje wywołanie starej nazwy. Po to istnieje właściciel integracji.

Właściciel integracji partii ustala kolejność merge na podstawie depends_on z kart zadań, rebase’uje każdy branch na najnowszy domyślny branch przed merge (albo prosi o to agenta) i uruchamia pełny zestaw testów na połączonym wyniku, a nie tylko checki danego zadania. Jeśli twoja platforma oferuje merge queue, używaj jej: testuje każdy pull request razem z tymi zmergowanymi przed nim, czyli dokładnie sprawdza konflikty semantyczne. Właściciel zatwierdza połączony wynik, a nie każdy diff z osobna.

Uruchamiaj sprawdzenie nakładania się przed merge, żeby konflikty wychodziły, póki są jeszcze decyzją, a nie niespodzianką:

scripts/agent-overlap.sh
#!/usr/bin/env bash
# Wypisz każdy plik, który zmienia więcej niż jeden otwarty branch agent/*.
set -euo pipefail
git fetch --quiet origin
for head in $(gh pr list --state open --limit 200 --json headRefName \
--jq '.[].headRefName' | grep '^agent/'); do
git diff --name-only "origin/main...origin/$head" | sed "s|\$| origin/$head|"
done | sort | awk '{ n[$1]++; b[$1] = b[$1] " " $2 }
END { for (f in n) if (n[f] > 1) print f ":" b[f] }'

Pętla czyta otwarte pull requesty, a nie zdalne branche, więc zmergowany branch, którego nikt nie usunął, nie pojawi się jako nakładanie się. Pusty wynik oznacza, że żadne dwa otwarte branche nie dotykają tego samego pliku. Linia taka jak src/billing/client.ts: origin/agent/billing-retry origin/agent/invoice-pdf oznacza, że zadanie złamało swoją kartę; właściciel decyduje, który branch zachowuje zmianę, a który robi rebase.

Zastąp BRANCH_A i BRANCH_B slugami obu zadań. Sprawdzasz listę z kroku 2 i zielony pełny zestaw testów, a nie scalony diff linia po linii.

Udowodnij, że równoległość się opłaca względem szeregowego baseline

Dział zatytułowany „Udowodnij, że równoległość się opłaca względem szeregowego baseline”

Zespół, który liczy działające agenty albo otwarte pull requesty, zawsze dojdzie do wniosku, że więcej znaczy lepiej. Mierz to, co dociera na produkcję, i porównuj z tym samym zespołem pracującym nad jednym zadaniem naraz.

MetrykaDefinicjaŹródło
Zaakceptowany throughputZmergowane pull requesty agentów na inżyniera tygodniowo, które przechodzą CI i nie zostały cofnięte ani ponownie otwarte w ciągu 14 dnigh pr list --state merged plus wyszukiwanie revertów
Wskaźnik reworkuOdsetek zmergowanych pull requestów agentów, które w ciągu 14 dni wymagają poprawkietykieta rework nadawana przez właściciela integracji
Wskaźnik konfliktówOdsetek pull requestów agentów, które przed merge wymagały rozwiązania konfliktuetykieta conflict albo log sprawdzenia nakładania się
Czas w reviewOd oznaczenia jako gotowy do review do merge, mediana i 90. percentyl; PR agentów startują jako drafty przy dispatchuzdarzenie ready_for_review na osi czasu każdego pull requesta; zobacz Kolejkę code review
Porzucona pracaBranche lub worktrees agent/ bez commita od siedmiu dnigit for-each-ref --sort=-committerdate refs/remotes/origin/agent/

Porównanie przeprowadź jako dwutygodniowy eksperyment na jednej klasie zadań. W pierwszym tygodniu każdy inżynier prowadzi jedno zadanie agenta naraz. W drugim klasa działa z proponowanym limitem. Rodzaj zadań i recenzenci pozostają bez zmian. Podnieś limit tylko wtedy, gdy zaakceptowany throughput na inżyniera wzrósł i wskaźnik reworku, wskaźnik konfliktów oraz czas w review zostały w granicach twoich celów. Jeśli throughput wzrósł, a rework razem z nim, dodatkowy output nie był zaakceptowaną pracą; utrzymaj albo obniż limit.

Zastąp START_DATE pierwszym dniem okna w formacie YYYY-MM-DD, a BASELINE tabelą z pierwszego tygodnia. Rekomendacja to propozycja; limit ustala tech lead.

Zacommituj politykę obok skryptów, żeby agenty i ludzie czytali te same zasady. Dostosuj liczby do swoich pomiarów; to struktura daje dowody do Q14.

docs/parallel-work.md
# Polityka równoległej pracy agentów
Właściciel: TECH_LEAD · Przegląd co dwa tygodnie · Ostatnia zmiana: DATE
## Kwalifikacja
- Równolegle działają tylko klasy zadań oznaczone w tabeli klas jako Równolegle.
- Klasy szeregowe (współdzielony kontrakt, migracja, aktualizacja zależności,
zmiana przekrojowa) działają pojedynczo, a zależne zadania czekają na ich merge.
- Bez karty zadania nie ma dispatchu: posiadane ścieżki, checki, warunki stopu,
właściciel integracji.
## Izolacja
- Każde zadanie startuje przez scripts/agent-task.sh: własny worktree, branch
agent/<slug>, blok portów, nazwa bazy danych. Żadnego wspólnego dev servera ani bazy.
- Sekrety pochodzą z zatwierdzonej lokalnej konfiguracji, nigdy nie są kopiowane od kolegi.
## Limity
- Limit WIP review zespołu: N otwartych PR agentów (wyliczony w arkuszu kolejki review).
- Limity per klasa są w tasks/caps.txt, po jednej parze „klasa limit” w wierszu;
każda klasa szeregowa ma limit 1.
- scripts/agent-task.sh jest bramką dispatchu: odrzuca nową pracę, gdy klasa
osiągnęła limit, a każdy PR agenta ma etykietę class:<nazwa>.
## Integracja
- Każda partia ma właściciela integracji. Ustala kolejność merge, uruchamia
pełny zestaw testów na połączonym wyniku i go zatwierdza.
- scripts/agent-overlap.sh działa przed każdym merge; każde nakładanie się go blokuje.
## Pomiar i sprzątanie
- Co tydzień: zaakceptowany throughput, rework, konflikty, czas w review, porzucona praca.
- Worktrees i branche agent/* bezczynne od 7 dni są przeglądane i usuwane
bez --force.

Zastąp TECH_LEAD, DATE i N własnymi wartościami, a liczby per klasa wpisz do tasks/caps.txt. Pięć sekcji odpowiada jeden do jednego odpowiedzi za pełne punkty w Q14, więc dowodem w scorecardzie jest link do tego pliku.

Co się psuje, gdy zespół uruchamia agenty równolegle

Dział zatytułowany „Co się psuje, gdy zespół uruchamia agenty równolegle”

Liczba pull requestów rośnie, a releasów nie przybywa. Niedokończona praca piętrzy się w review, a dashboardy wyglądają zdrowo. Naprawa: wstrzymaj dispatch dla klasy, opróżnij kolejkę poniżej limitu, potem wznów pracę z ostatnim limitem, przy którym zaakceptowany throughput był najwyższy.

Dwa zielone branche razem psują build. Konflikt semantyczny przeszedł checki każdego zadania osobno. Naprawa: cofnij późniejszy merge, dodaj test, który ujawnia konflikt, i wymagaj pełnego zestawu testów na połączonym wyniku — przez właściciela integracji albo merge queue.

Testy przechodzą albo nie, zależnie od tego, kto jeszcze pracuje. Worktrees izolowały pliki, ale nie port, bazę danych ani cache. Naprawa: przenieś każde zadanie na skrypt izolacji, a polecenia dev i testów niech czytają .agent-env zamiast wartości domyślnych.

Agent edytuje współdzielony kontrakt, żeby dokończyć zadanie. read_only_paths z karty były radą, a nie bramką. Naprawa: cofnij zmianę kontraktu, zrób z niej zadanie szeregowe i uruchamiaj w CI sprawdzenie zakresu dla każdego pull requesta z brancha agent/:

scripts/agent-scope.sh
#!/usr/bin/env bash
# Zakończ błędem, gdy branch agent/<slug> zmienia plik spoza owned_paths swojej karty.
# Blokuje domyślnie: brak karty, puste owned_paths albo nieudany diff to błąd.
set -euo pipefail
slug="${1:-${GITHUB_HEAD_REF:?pass SLUG or run on a pull request}}"
slug="${slug#agent/}"
head="${2:-HEAD}"
card="$(git show "origin/main:tasks/$slug.yaml")" || { echo "no card tasks/$slug.yaml on origin/main"; exit 1; }
mapfile -t globs < <(yq -r '.owned_paths[]' <<<"$card")
(( ${#globs[@]} > 0 )) || { echo "card has no owned_paths"; exit 1; }
changed="$(git diff --name-only "origin/main...$head")" || { echo "cannot diff origin/main...$head"; exit 1; }
status=0
while read -r file; do
[[ -z $file ]] && continue
[[ $file == tasks/* ]] && { echo "task card edited: $file"; status=1; continue; }
for glob in "${globs[@]}"; do [[ $file == $glob ]] && continue 2; done
echo "outside owned_paths: $file"; status=1
done <<<"$changed"
exit "$status"

Skrypt czyta kartę z origin/main, a nie z brancha, i kończy się błędem przy każdej zmianie w tasks/, więc agent nie poszerzy sobie zakresu, edytując własną kartę. Domyślnie też blokuje: brak karty, puste owned_paths albo diff, którego nie da się policzyć, to błąd, a nie zielone światło. Sam skrypt nie jest jednak odporny na manipulację. Przy wyzwalaczu pull_request GitHub uruchamia plik workflow z samego pull requesta, więc branch w tym samym repozytorium może zmienić job tak, żeby pominął krok albo uruchomił własną kopię skryptu, a check i tak będzie zielony. Prawdziwą kontrolą jest review: wpis w CODEOWNERS, który przypisuje .github/, scripts/agent-scope.sh i tasks/ właścicielom integracji, oraz reguła ochrony brancha (albo ruleset) na main, która wymaga review od code ownera i czyni ten check obowiązkowym. Wtedy branch, który dotyka bramki, nie zostanie zmergowany bez osoby, która za nią odpowiada. Na pull_request daj jobowi permissions: contents: read, żadnych sekretów i persist-credentials: false przy checkoucie. Alternatywą jest pull_request_target, który zawsze uruchamia workflow z domyślnego brancha. Taki job działa z tokenem repozytorium bazowego, więc zostaw go przy contents: read, bez sekretów, zrób checkout wyłącznie main z persist-credentials: false i pobierz head pull requesta jako dane, bez checkoutu i bez uruchamiania czegokolwiek z niego:

.github/workflows/agent-scope.yml
on:
pull_request_target:
branches: [main]
permissions:
contents: read
jobs:
scope:
if: startsWith(github.head_ref, 'agent/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: main
fetch-depth: 0
persist-credentials: false
- name: Fetch the pull request head as data
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ github.token }}
run: |
auth="$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 -w0)"
echo "::add-mask::$auth"
git -c http.extraheader="AUTHORIZATION: basic $auth" fetch origin "refs/pull/$PR_NUMBER/head"
- run: bash scripts/agent-scope.sh "$GITHUB_HEAD_REF" FETCH_HEAD

Fetch korzysta z refs/pull/<number>/head, który istnieje także dla pull requestów z forków, gdzie nazwy brancha nie ma w origin. Token tylko do odczytu trafia wyłącznie do tego jednego git fetch przez -c http.extraheader i nigdy nie jest zapisywany w .git/config; w publicznym repozytorium nagłówek można pominąć. Do mapfile potrzebny jest bash 4 lub nowszy (runnery CI go mają; na macOS zainstaluj bash przez Homebrew) i yq v4. W dopasowaniu wzorców w bashu * przechodzi przez /, więc src/billing/retry/** obejmuje całe drzewo katalogu. Pobierz historię wystarczającą, żeby diff z trzema kropkami znalazł merge base (w GitHub Actions fetch-depth: 0).

Worktrees i branche się mnożą. Dyski się zapełniają, a nieaktualne branche zaśmiecają każdą listę agent/* i raport bezczynności. Naprawa: co tydzień uruchamiaj raport siedmiodniowej bezczynności, usuwaj zmergowane i porzucone elementy poleceniami bez --force i zapisuj, kto zatwierdził każde usunięcie.

Inżynierowie nadzorują więcej sesji, niż są w stanie śledzić. Review zamienia się w bezrefleksyjne zatwierdzanie. Naprawa: zmniejsz liczbę sesji na inżyniera i rotuj dyżur review, jak opisuje sekcja o zrównoważonym tempie w Kolejce code review.

Równoległość jest stabilna, gdy limity trzymają się przez miesiąc bez rosnącego reworku. Następny szczebel drabiny to praca, która działa bez człowieka obserwującego sesję — ma to sens dopiero wtedy, gdy bramki z tej strony są na miejscu.