Wstęp: bramka, która odmawia „gotowe”
Bramka, w rozumieniu Shopify, to kontrola, którą zmiana napisana przez AI musi przejść, zanim pójdzie dalej. Odtworzona w Claude Code, opłaciła się tylko pierwsza: testy akceptacyjne. Shopify opublikowało cztery bramki i żadnych liczb, więc ten eksperyment buduje je na prawdziwym repozytorium, uruchamia pięćdziesiąt zleceń z kolejno wyłączaną każdą bramką i liczy, co każda z nich wychwyciła i ile kosztowała.
Helix stał się publiczny 21 września 2026. Każdy checkpoint przechodzi przez cztery bramki po kolei: testy, przegląd designu, dwóch recenzentów i inżynier. Post nie podaje żadnego odsetka zaliczeń. Recenzenci to część, którą większość ludzi kopiuje, a w tych pięćdziesięciu runach odsetek zaliczeń ruszał się tylko wtedy, gdy w pętli były testy. Recenzenci nic nie dodali, za mniej więcej trzykrotnie wyższą cenę.
Z tego samego stanowiska testowego wychodzą jeszcze dwa wnioski. Hook, którego rzekomo nie da się obejść, ma we własnej dokumentacji trzy wyjścia. A gdy testy i recenzenci się nie zgadzali, agent zrobił coś, czego nikt nie zaplanował. Liczenie zaczyna się od przypadku bez żadnej bramki.
Cztery bramki, zero liczb, jedno stanowisko testowe
Repozytorium testowe to tinydb, prawdziwa baza dokumentów, z 226 zielonymi testami na użytym commicie.
Helix to wewnętrzne narzędzie Shopify do przenoszenia jego aplikacji mobilnych z powrotem na kod natywny. Post formułuje regułę wprost: każdy checkpoint przechodzi przez cztery bramki, po kolei.
| Bramka | Co robi w poście Shopify |
|---|---|
| Zachowanie | Uruchamia testy, w których musi przejść każdy istotny przypadek |
| Design | Prosi drugi model, by działał jak perfekcjonistyczny recenzent designu |
| Przegląd | Dwóch niezależnych agentów recenzentów z izolowanym kontekstem; pętla powtarza się, aż obaj zatwierdzą |
| Inżynier | Pętlę zamyka człowiek |
Zdanie, które sprzedaje ten pomysł, to „An attempt is allowed to be wrong. It is not allowed to ship until it isn't.” Bramka designu odpada w bibliotece, a post mówi, że zmiana czysto logiczna po prostu ją pomija.
Czego post nigdy nie podaje, to liczby. Aplikację Shop przebudowano w 12 tygodni, a aplikacja Shopify ma ponad 300 ekranów i to cały rejestr. Nie ma odsetka zaliczeń, kosztu ani liczby defektów, które uciekły.
Stanowisko testowe to pięć zleceń napisanych tak, jak pisze je programista: operacje push i pull, zapytanie between, zmiana nazwy tabeli, wyszukiwanie z sortowaniem i rozmiar cache zapisu. Za każdym zleceniem stoi pięć ukrytych wymagań, których agent nigdy nie widzi, czyli rzeczy, których oczekiwałby staranny starszy inżynier. Daje to w sumie 25 wymagań.
Workerem jest Sonnet 5.5 uruchamiany w trybie headless. Pięć konfiguracji razy pięć zadań razy dwa powtórzenia daje 50 runów. Jedno zastrzeżenie: jawne wersje tych zleceń przechodzą za jednym razem, więc prompty skrócono do tego, co ludzie faktycznie wpisują.
Bez żadnej bramki przechodzi 35 z 50 wymagań. To 70%, przy czym trzy runy na dziesięć są w pełni czyste.
Zadanie z sortowaniem pokazuje, jak wyglądają pudła. Agent zaindeksował każdy dokument polem sortowania i gdy tylko jednemu dokumentowi go brakuje, całe wyszukiwanie się wywraca. Pięć wymagań, zero zaliczonych, jeden brakujący klucz.
Hook, który odmawia „gotowe”, i jego trzy wyjścia
Hook Stop to skrypt, który Claude Code uruchamia za każdym razem, gdy agent próbuje zakończyć turę. Własny poradnik Anthropic nazywa problem, który to rozwiązuje: Claude kończy, gdy praca wygląda na skończoną. Bez kontroli, którą może uruchomić, „wygląda na skończoną” to jedyny dostępny sygnał, a ty stajesz się pętlą weryfikacji.
Hook mieszka w pliku ustawień projektu, pod hooks, Stop, jako jedno polecenie z timeoutem. Tutaj jest to jednolinijkowy wrapper, który wywołuje skrypt bramki. Kontraktem jest kod wyjścia: 0 pozwala zakończyć turę, 2 ją blokuje, a wszystko, co skrypt wypisał na standardowe wyjście błędów, wraca do agenta jako powód.
W praktyce wygląda to tak. Agent podsumowuje swoją zmianę i kończy. Hook uruchamia testy akceptacyjne, jeden pada i tura się nie kończy. Agent czyta porażkę jako informację zwrotną z hooka, tutaj jest to test, który oczekiwał błędu typu i go nie dostał, poprawia kod i kończy ponownie. Tym razem bramka milczy.
Reguła w prompcie to rada. Hook nią nie jest. A jednak ta sama strona dokumentacji wymienia trzy drogi wyjścia.
| Wyjście | Co mówi dokumentacja |
|---|---|
| Limit | Gdy hooki stop kontynuowały turę 8 razy z rzędu, Claude Code nadpisuje następną blokadę i kończy turę. Licznik zeruje się za każdym razem, gdy agent wywoła narzędzie, a zmienna środowiskowa go podnosi. |
| Timeout | Domyślnie 600 sekund dla hooka typu command, a hook, który przekroczy timeout, nie wydaje żadnej decyzji. Wolny zestaw testów sam otwiera bramkę. |
| Flaga | Wejście hooka niesie stop_hook_active, ustawione na true, gdy sesja już kontynuuje z powodu hooka stop. Dokumentacja każe to sprawdzać, by nigdy nie blokować z powodu warunku, którego nie da się spełnić. |
Bramka z tego eksperymentu loguje tę flagę i ją ignoruje. Poddaje się sama po sześciu blokadach, dzięki czemu pętle da się policzyć.
Jest jeszcze jedno wyjście, o którym nikt nie pisze w dokumentacji. Po jednej blokadzie agent zaczął szukać testów: uruchomił cat na skrypcie hooka i przeszukał cały dysk w poszukiwaniu pliku z testami. Zatrzymała go dopiero lista dozwolonych. Dlatego testy akceptacyjne leżą poza drzewem repozytorium, ustawienia zabraniają odczytu hooka i ukrytych testów, a agent widzi porażkę, ale nigdy plik.
Bramka 1: testy akceptacyjne
Bramka testów akceptacyjnych to zestaw testów, napisanych niezależnie od implementującego, który definiuje, co znaczy „gotowe”. Pierwsza bramka Shopify to nie są własne testy agenta. Subagent generuje dla każdego checkpointu przypadki testowe w stylu integracyjnym, naciskając na przypadki brzegowe wykraczające poza szczęśliwą ścieżkę, i to te testy określają, co znaczy udowodnione.
Piszący jest więc tutaj osobnym Sonnetem w trybie headless. Widzi zlecenie i repozytorium sprzed zmiany, tylko do odczytu, nie może uruchamiać kodu i ma napisać jeden test na każde zachowanie, którego oczekiwałby staranny starszy inżynier: brakujące pola, złe typy, wartości graniczne, trwałość danych, cache i niezmienione dotychczasowe zachowanie. Pisze od 23 do 53 testów, średnio 39, w około 40 sekund. Plik ląduje poza repozytorium, więc gdy test pada, agent czyta asercję i nigdy sam test.
Same testy podnoszą odsetek zaliczeń z 70% do 84%: 42 z 50 wymagań i sześć w pełni czystych runów zamiast trzech.
| Zadanie | Bez testów | Z testami |
|---|---|---|
| Wyszukiwanie z sortowaniem | 5 z 10 | 10 z 10 |
| Rozmiar cache zapisu | 8 z 10 | 10 z 10 |
| Push i pull | 4 z 10 | 4 z 10 |
Awaria przy braku pola w zadaniu z sortowaniem to dokładnie ten rodzaj przypadku, o który pyta piszący. W zadaniu z cache piszący zażądał błędu wartości dla rozmiaru cache poniżej 1.
Zadanie push i pull się nie rusza. Brakuje mu trzech wymagań, z których dwa dotyczą dokumentu bez tego pola, a zlecenie nic o tym nie mówiło. Piszący asekuruje się dokładnie tam, gdzie prompt milczy: jego test akceptuje zarówno wyjątek, jak i ciche nic nierobienie, więc bramka nie może odzyskać wymagania, którego nikt nie sformułował.
W 30 runach z bramką testów bramka odesłała agenta 28 razy z faktycznie niespełnionym wymaganiem i pięć razy, gdy wszystkie były spełnione. Jednym z tych pięciu było zadanie ze zmianą nazwy: piszący chciał odrzucać nazwy, które nie są ciągami znaków, nikt o to nie prosił, a agent stracił przez to rundę.
Pierwsza wersja tej bramki uruchamiała ponownie własne testy implementującego. Przepuszczała każdy run przy pierwszym zatrzymaniu, choć ukryte wymaganie padało, bo własne testy agenta testują to, co sam zbudował.
Cena bramki to $0.20 za run zamiast $0.085 i 60 sekund zamiast 33.
Bramka 3: dwóch recenzentów, tak jak napisał to Shopify
Bramka przeglądu to para agentów recenzentów, z których każdy pracuje w świeżym kontekście. Sformułowanie Shopify: dwóch niezależnych agentów recenzentów z izolowanym kontekstem sprawdza nowy kod, każde ustalenie trzeba poprawić, a pętla powtarza się, aż obaj recenzenci zatwierdzą. Poradnik Anthropic wyjaśnia, dlaczego izolacja ma znaczenie: recenzent ze świeżym kontekstem widzi tylko diff i kryteria, które mu podasz, a nie rozumowanie, które do zmiany doprowadziło.
Każdy recenzent to tutaj Sonnet w trybie headless z narzędziami tylko do odczytu i co najwyżej 15 turami. Zwraca werdykt jako ustrukturyzowany JSON: flagę approve oraz ustalenia, każde z wagą blocking albo minor. Dwóch recenzentów działa równolegle na zleceniu i pełnym diffie, ich ustalenia są łączone, a bramka blokuje, gdy którykolwiek znajdzie coś blokującego. Para kosztuje od 3 do 6 centów na jedno zatrzymanie.
| Konfiguracja | Zaliczone wymagania | Odsetek zaliczeń | Blokady |
|---|---|---|---|
| Bez bramki | 35 z 50 | 70% | brak |
| Sami recenzenci | 34 z 50 | 68% | 1 na 10 runów |
| Same testy | 42 z 50 | 84% | 6 na 10 runów (3 słuszne, 3 fałszywe) |
| Recenzenci po testach | 41 z 50 | 82% | 2 na 10 runów, obie słuszne |
Sami recenzenci lądują dwa punkty poniżej braku bramki, a recenzenci po testach dwa punkty poniżej samych testów.
Zadanie z sortowaniem wyjaśnia ten wynik. W jednym runie obaj recenzenci zablokowali: sortowanie indeksuje dokument, jeden dokument bez tego pola zgłasza błąd klucza i całe wyszukiwanie zawodzi. Agent to poprawił i zaliczył pięć z pięciu.
W dwóch innych runach obaj recenzenci zobaczyli dokładnie ten sam crash i ocenili go jako minor. Dokument bez pola sortowania zgłasza goły błąd klucza, a docstring to dokumentuje. Obaj recenzenci zatwierdzili i oba runy skończyły się na zero z pięciu. Ten sam model, ten sam diff, ten sam błąd, dwie różne wagi. Recenzenci nigdy nie mylili się, gdy blokowali. Problemem jest to, co przepuszczają.
Sierpniowa publikacja zmierzyła taki sam układ na publicznym benchmarku. Zero-shot uzyskał 77%, jeden recenzent 77%, a dwóch niezależnych recenzentów 75%, wszystko w granicach szumu względem braku przeglądu.
| Konfiguracja na LiveCodeBench | Wynik |
|---|---|
| Zero-shot | 77% |
| Jeden recenzent | 77% |
| Dwóch niezależnych recenzentów | 75% |
| Recenzent plus krytyk (przegląd adwersaryjny) | 87% |
Recenzent plus krytyk i pętla bez końca
Przegląd adwersaryjny to recenzent sparowany z krytykiem, który audytuje przegląd przez ustrukturyzowaną niezgodę, zanim główny agent cokolwiek zmieni. Ta sama publikacja uznała, że to układ, który działa. Uzyskał 87% tam, gdzie dwóch recenzentów uzyskało 75%, i pokonał najmocniejszy punkt odniesienia z pięcioma agentami, który miał 82%.
| Benchmark | Zero-shot | Z przeglądem adwersaryjnym | Koszt |
|---|---|---|---|
| SWE-bench | 71.6 | 75.2 | około 4.5x więcej tokenów |
Krytyk trafił więc do bramki. Recenzent A recenzuje. Krytyk B dostaje ustalenia plus diff i oznacza każde jako podtrzymane albo odrzucone. Bramkę zamyka tylko podtrzymane ustalenie blokujące.
W tym repozytorium wynik to 39 z 50, czyli 78%. Para z krytykiem nie zablokowała ani razu w dziesięciu runach. Podczas walidacji odrzuciła fałszywe ustalenie, na którym układ z dwoma recenzentami by zablokował.
Czego krytyk nie naprawił, to pętla. Trzy runy dobiły do sześciu blokad na tym samym teście akceptacyjnym i bramka się poddała, a prawdziwe wymaganie wciąż padało przy każdym zatrzymaniu. W dwóch z nich blokujący test był błędem samego piszącego i agent to powiedział: nie zmieni kodu pod test, którego nie może przeczytać.
Trzeci przypadek warto zapamiętać. Test akceptacyjny chciał, by nieudana aktualizacja zostawiała każdy dokument nietknięty, więc agent przepisał metodę aktualizacji tabeli. Recenzenci to zablokowali, bo zmiana w metodzie aktualizacji nie była zlecona, a zadaniem były dwie nowe funkcje. Agent cofnął zmianę. Jego ostatnia wiadomość mówiła, że test akceptacyjny i przegląd kodu żądają przeciwnych rzeczy, więc zatrzymał się, niczego nie zmieniając. Sześć blokad później licznik go wypuścił.
Punkt poddania się jest tutaj ustawiony na sześć. Własny limit Claude Code to osiem kolejnych blokad zerowanych przy każdym wywołaniu narzędzia, więc w pięćdziesięciu runach nie zadziałał ani razu.
Bramka 4 to ty: co dotarło do człowieka, rachunek, zestaw
Czwartą bramką jest inżynier, a post Shopify obiecuje, że bramki nie robią się łagodniejsze, gdy nikt nie patrzy. Do inżyniera trafia to, co przepuściły pozostałe bramki. Poniższy rejestr odpowiada na pytanie, która bramka się zwróciła.
| Konfiguracja | Zaliczone z 50 | Odsetek zaliczeń | Koszt za run | Zostaje dla człowieka |
|---|---|---|---|---|
| Bez bramki | 35 | 70% | $0.085 | 15 |
| Testy akceptacyjne | 42 | 84% | $0.20 | 8 |
| Sami recenzenci | 34 | 68% | $0.15 | 16 |
| Testy z recenzentami | 41 | 82% | $0.28 | 9 |
| Testy z recenzentem plus krytykiem | 39 | 78% | $0.28 | 11 |
Każdy wiersz z bramką zajmuje od 54 do 86 sekund na run, wobec 33 bez bramki.
Blokady we wszystkich pięćdziesięciu runach rozkładają się tak. Testy odsyłały agenta 28 razy słusznie i pięć razy niesłusznie. Recenzenci zablokowali trzy razy, nigdy niesłusznie, a trzy pętle się poddały. Cała macierz kosztowała $9.95 wydatków API po cenach katalogowych, według raportu CLI, i około 3,136 sekund, czyli 52 minuty, zsumowanego czasu workerów.
Werdykt dla repozytorium takiego jak to: zachowaj bramkę testów, z piszącym, który nie jest implementującym, i testami, których agent nie może edytować. Spodziewaj się mniej więcej jednej fałszywej blokady na sześć i daj pętli licznik. Bramka z dwoma recenzentami w wersji napisanej przez Shopify nie ruszyła tu odsetka zaliczeń, w żadnym z dwóch układów. Jeśli ją zachowasz, zachowaj jako radę w informacji zwrotnej, nie jako poprzeczkę.
Jedna pułapka po drodze. Dokumentacja opisuje goły przełącznik, który pomija hooki, skille, pluginy i pamięć w wywołaniach skryptowych. Przy logowaniu subskrypcją czyta on tylko klucz API i zwraca błąd, więc każdy recenzent jest izolowany za pomocą pustego źródła ustawień i wyłączonych hooków.
Ograniczenia są realne: jedno repozytorium, jeden model workera, pięć zadań, dwa powtórzenia na komórkę. Różnica dwóch punktów między wierszami to szum. Czternaście punktów między brakiem bramki a bramką testów nim nie jest. Bramki designu nie zbudowano, a inżyniera nie mierzono czasowo.
Z czterech bramek Shopify, na tym repozytorium, opłaciły się tylko testy. Cały zestaw jest w darmowym pakiecie: plik ustawień z hookiem i listą deny, skrypt stop, bramka, polecenia piszącego i recenzentów z ich schematami oraz szablon rejestru.
AIDive