Strona główna / Artykuły

Harness do produkcji dla Claude Code: sześć warstw od Slacka do połączonego MR

Jak sześciowarstwowy harness przekształca zgłoszenia z Slacka w połączony kod: przyjmowanie zdarzeń, pętle agentów z kontrolą dostępu, trwały stan, samoregeneracja, możliwość obserwacji oraz kontrola operatora.

Wydawca: REACTAPP.TOP42 min czytania9771 słów

Źródła: Building a Production Agent Harness: Turning Claude Code Into a Multi-Agent Engineering Pipeline

Jeśli umieścić model kodujący samodzielnie, otrzymuje się „mózg w słoiku”. Potrafi pisać kod i wywoływać funkcje bez problemu, ale nie będzie w stanie zobaczyć twojej wiadomości prywatnej w Slacku o 3 rano, ponowić próby zakończonej błędem w CI, rozwiązać konflikt fuzji, który właśnie pojawił się w otwartym wniosku o fuzję, ani przypomnieć sobie, że jakiś recenzent zadał pytanie wczoraj, a nikt nie odpowiedział.

To, co przekształca model w coś użytecznego, to specjalna infrastruktura: ramy działania w czasie rzeczywistym, które dostarczają „zmysłów”, możliwości działania i pamięci. Składają się one z obsługi zdarzeń, orkiestracji agentów, trwałego przechowywania stanu, pętli, które same się naprawiają, funkcji obserwowalności oraz sposobu umożliwiającego operatorowi ludzkiemu kierowanie procesem. Opisany poniżej system jest produkcyjny, a nie tylko demonstracją badawczą. Monitoruje kanał w Slacku, otwiera wnioski o fuzję w pięciu wewnętrznych repozytoriach, odpowiada na komentarze recenzentów w nocy i samodzielnie naprawia błędne prace w CI, bez żadnych prośb.

Jest to wewnętrzny, wieloagentowy pipeline zbudowany na bazie Claude Code. Działa nieprzerwanie od kilku miesięcy, przyjmując zadania z Slacka i przekazując je do realizacji w ramach rzeczywistej pracy inżynieryjnej. Poniżej przedstawiono ogólny obraz: co robi, w jaki sposób połączone są jego komponenty oraz czynniki wpływające na projekt.

Rozdziały są uporządkowane w następującej kolejności: dlaczego w ogóle potrzebny jest taki system, co robi ten konkretny rozwiązanie, trzy luki, które eliminuje, sześć warstw (przyjmowanie zdarzeń, orkiestracja agentów ze szczególnym uwzględnieniem autonomicznego cyklu i warstwy samodoskonalenia, trwały stan, samoregeneracja, możliwości obserwacji oraz kontrola przez człowieka), kilka dodatkowych elementów poza warstwami, szczegółowy przegląd procesu od początku do końca oraz porównanie z innymi takimi systemami.

Dlaczego okno czatu i kilka narzędzi to za mało

Gdy w 2023 roku pojawiły się pluginy do asystentów czatowych, wiele zespołów wypróbowało oczywisty skrót: umieściło interfejs czatu przed modelem LLM, dało mu kilka narzędzi do wywoływania funkcji i nazwało wynik „agentem inżynieryjnym”. Taka konfiguracja prawie działa w demonstracji, ale nigdy nie przetrwa w środowisku produkcyjnym. W ciągu tygodnia pojawiają się trzy problemy.

  1. Kontekst znika, gdy użytkownik zamyka kartę. Pamięć musi przetrwać dłużej niż jedna sesja czatowa. Prawdziwe zadanie inżynieryjne trwa dni: otwarcie zgłoszenia, utworzenie gałęzi, napisanie projektu, jego sprawdzenie, odpowiedź na komentarze, oczekiwanie na pozytywny wynik testów CI oraz połączenie gałęzi. Żaden pojedynczy wywołanie modelu nie może pomieścić tego wszystkiego.
  2. Nic nie reaguje na zmiany z zewnątrz. Ktoś komentuje o godzinie 16:00. Testy CI zawodzą przy trzecim commitie. Kolega z zespołu odpisuje w tym temacie. Agent musi reagować na te wydarzenia, zamiast bez końca sprawdzać stan lub czekać, aż ktoś ponownie go aktywuje.
  • Błędy nigdy nie są naprawiane. Agent próbuje dalej, CI przestaje działać, a operator musi wrócić następnego dnia i wyjaśnić błąd od początku. Albo sesja agenta z dostawcą tożsamości wygasa w trakcie zadania, przez co cały proces zawodzi bez żadnego sygnału. Oba te przypadki zdarzają się codziennie, przez co inżynierowie są wzywani wieczorami i w weekendy, by zajmować się pracą, którą agent miał wykonać.
  • Naprawa tych problemów to zadanie narzędzia typu harness. To właśnie ono odróżnia zwykłą kartę czatu od samego Claude Code, od agentów w tle Cursora, od Devin lub od opisanego tutaj systemu. Model leżący u ich podstaw jest w większości wymienny pomiędzy nimi. To harness otaczający go jest tym, co się różni.

    Model ma dostęp do świata wyłącznie przez harness, a to właśnie harness wykonuje niemal całą pracę inżynieryjną niezbędną, by model językowy zachowywał się jak system.

    Zadanie, do którego zostało stworzone narzędzie

    Pierwotny problem był konkretny. Kanał Slack o nazwie #support zbiera około 30 zgłoszeń tygodniowo. Każde z nich wymaga zbadania, a zazwyczaj także zmiany w kodzie w jednym z pięciu repozytoriów. Rozpatrywanie zgłoszenia trwa kilka dni. Recenzenci komentują propozycje zmian, systemy CI dają błędy, a niektóre badania okazują się powtórzeniem tych z poprzedniego miesiąca. Zespół zajmujący się tym obciążeniem był ciągle wzywany wieczorami i w weekendy, a sytuacja się nie poprawiała, ponieważ co cykl pojawiały się te same problemy.

    Narzędzie ma trzy zadania, uporządkowane według stopnia ingerencji:

    1. Zbadanie. Gdy do kanału #support trafia nowe zgłoszenie, należy prześledzić jego historię, zebrać informacje z różnych repozytoriów i opublikować ustrukturyzowaną analizę w tym samym temacie.
  • Poprawka. Gdy operator ją zatwierdzi, należy otworzyć prośbę o połączenie z zaproponowaną zmianą, odpowiedzieć na komentarze recenzentów, obserwować proces CI, naprawić błędy i doprowadzić do połączenia zmian.
  • Samoudoskonalanie. Każdy zamknięty przypadek dostarcza informacji zwrotnych. Jeśli powtarza się pewien wzorzec – na przykład pięć zgłoszeń w jednej dziedzinie dotyczących tej samej konfiguracji – system identyfikuje braki i proponuje zmiany we własnym kodzie.
  • To trzecie zadanie odróżnia ten system od zwykłych botów do obsługi zgłoszeń. Surowcem tego systemu jest jego własny kod źródłowy, a model LLM może na niego wpływać poprzez prośby o połączenie, które przechodzą przez ludzką weryfikację.

    System składa się z sześciu warstw logicznych, z których każda ma oddzielną sekcję poniżej.

    Trzy luki, których model sam nie może zamknąć

    Cała konstrukcja opiera się na jednej zasadzie: model LLM bez stanu, działający wyłącznie reaktywnie, nie może być systemem produkcyjnym. Sześć warstw istnieje po to, by zamknąć trzy luki, których model sam nie jest w stanie załatać.

    Pierwszą lukią jest reaktywność. Model nie może sam się uruchomić. Warstwa 1 (przyjmowanie zdarzeń) zajmuje się tym, ponieważ informacje od Slacka, wyniki GitLab CI oraz strony PagerDuty trafiają wszystkie do jednej kolejki dyspozycyjnej. LLM nigdy nie sprawdza stanu samodzielnie – jest wywoływany.

    Drugą lukią jest trwałość. Modele zapominają wszystko pomiędzy sesjami i tracą kontekst, gdy praca przechodzi między maszynami; nie potrafią też odróżnić sytuacji, gdy coś zostało rozwiązane, od komitów, które faktycznie trafiły do repozytorium. Warstwa 3 (trwały stan) rozwiązuje ten problem: struktury pamięciowe eliminują duplikaty w obrębie jednego procesu, lokalne pliki JSON przechowują informacje operacyjne, a przestrzenie robocze zsynchronizowane z gitem zachowują trwały stan przypadków. Ciągłość sesji między maszynami, opisana później, również wychodzi z tej samej zasady – mianowicie że stan powinien znajdować się w git, a nie w działającym procesie.

    Trzecią lukią jest jakość. Gdy nie ma struktury, pętla rozumowania modelu językowego wyciąga pewne wnioski na podstawie słabych dowodów, uznaje dochodzenia za zakończone bez sprawdzenia wszystkich dostępnych źródeł oraz tworzy opisy zmian, które od pierwszego zdania brzmią, jakby zostały napisane przez maszynę. Warstwa 2 (orchestracja agentów) oraz pętla dochodzeniowa radzą sobie z tym za pomocą ustrukturyzowanych kontraktów wyjściowych, kontroli jakości przy każdej iteracji, przeglądu adwersarialnego po zakończeniu pętli oraz mechanizmów samonaprawczych w ramach zewnętrznego cyklu zmian kodu, testów ciągłych i łączenia zmian.

    Kumulacja jest tym, co łączy poszczególne warstwy, ponieważ każda zamknięta sprawa przyspiesza prace nad następna. Zatwierdzone dochodzenia stają się wpisami w bazie wiedzy, a zatwierdzone zmiany przekształcają się w wzorce, na które może wskazać analizator luk. Zamknięta sprawa otwiera możliwość stworzenia zmiany poprawkowej skierowanej na sam mechanizm zarządzania. Z każdym cyklem coraz mniej rodzajów zgłoszeń jest traktowanych niewłaściwie.

    Szczegół 1: jak zdarzenia uruchamiają system

    Trzy zewnętrzne źródła sygnałów mogą uruchomić ten mechanizm:

    • Wiadomości w Slacku, czyli wzmianki w kanałach, odpowiedzi w wątkach oraz polecenia administracyjne wysyłane jako prywatna wiadomość do samego siebie.
    • Aktywność w GitLabie, czyli komentarze pod propozycjami zmian, wyniki procesów oraz błędy w tych procesach.
    • Ostrzeżenia z PagerDuty, używane wtedy, gdy pracownik dyżurny wspomina o ticketu, który mechanizm już widział, aby można było przywołać wcześniejszy kontekst.

    Te trzy źródła różnią się opóźnieniem i niezawodnością, dlatego każde z nich ma własną ścieżkę wejścia.

    Dwa praktyczne szczegóły nigdy nie pojawiają się w standardowej wersji tego diagramu.

    Konsystencja ostateczna. Koniec API Slacka conversations.history może wykazywać opóźnienie od 1 do 30 sekund. Narzędzie sprawdzające, które widzi pusty wynik i naiwnie przesuwa wskaźnik do przodu, pominie na zawsze każdą wiadomość, której ts mieści się w tym oknie czasowym. W rezultacie wskaźnik zostaje zamarznięty przy pustych wynikach, a kolejna próba rozpoczyna się od tego samego najstarszego datownika, co się powtarza, dopóki nie pojawi się przynajmniej jedna wiadomość. Zbiór deduplikujący oparty na ts każdej wiadomości filtruje duplikaty powstające w wyniku tych prób.

    Socket Mode w połączeniu z mechanizmem pollingu stanowi pełne rozwiązanie. Socket Mode dostarcza odpowiedzi w czasie krótszym niż sekunda, ale połączenia WebSocket się rozrywają. Mechanizm pollingu pełni rolę bezpiecznika, łapiąc wszystko to, co umknęło Socket Mode, a oba rozwiązania korzystają z pliku socket-dedup, dzięki czemu żadna wiadomość nie jest wysyłana dwukrotnie. Bez tego wszystko, co przychodzi podczas przerwanej łączności, znika bez śladu, a operator nie otrzymuje żadnego sygnału o tym, że coś zostało przeoczone.

    Łuk cron w mr-monitor zachowuje się inaczej. GitLab nie wysyła komentarzy do recenzji do pipeline, więc jedyną opcją jest polling, a skalowanie samego polling okazało się trudnym zadaniem. Każdy dodatkowy długo trwający MR, który system przyjmuje, wydłuża 10-minutowy cykl, w którym gromadzą się wskaźniki dla nieaktywnych wątków, a każdy z nich generuje jedną próbę połączenia z API Slack na każdy cykl. Pozostawione bez interwencji powodowały ciągłe awarie typu 429, które hamowały istotne operacje polling. Poprawka polega na automatycznym usunięciu wskaźnika, gdy API nadawcze zwraca wartość thread_not_found, dzięki czemu wątek jest pomijany w kolejnym cyklu. Przed wprowadzeniem poprawki jeden cykl obejmował 12 prób ponawiania co godzinami, natomiast po niej już żadnych.

    Warstwa 2: jak koordynowane są agenty

    LLM faktycznie działa tutaj. Narzędzie uruchamia procesy jako tymczasowe jednostki systemd, a każdemu z nich przekazuje payload w formacie JSON z nazwą procesu do wykonania, katalogiem case_dir do użycia oraz wątkiem, do którego należy.

    Każda ścieżka przetwarzania odpowiada jednemu długiemu uruchomieniu Claude Code („mózgowi”), który otrzymuje dobrze przygotowany prompt oraz katalog case_dir nadający się do zapisu. Katalog ten służy jako przestrzeń robocza agenta, w której znajduje się plik TASK.md przypadku, coraz większa liczba plików typu followup_transcript_*.md z transkrypcjami, wszelkie raporty w formacie gap_report*.md tworzone przez agentów oraz pośrednie pliki w formacie JSON.

    Sposób sekwencjonowania agentów jest częścią projektu. Zamiast jednego agenta, który robi wszystko, łączy się kilka specjalistycznych agentów, a każdy z nich przekazuje zadanie w określonym momencie:

    • oncall_run przeprowadza pierwsze badanie, obejmujące do 20 iteracji rozumowania z kontrolą jakości, kończąc się wyraźną samokrytyką. Jego wynikiem jest plik ANALYSIS.md opisujący, co jest nie tak, oraz plik TASK.md opisujący, co należy zrobić.
    • case_followup to agent do rozmów długoterminowych. Operator może odpowiadać prywatnymi wiadomościami w temacie sprawy przez kilka dni, a każda odpowiedź ponownie uruchamia case_followup z całym dotychczasowym dialogiem. Co N odpowiedzi agent ten wywołuje również gap_analyzer jako podagenta, a ten cykl generuje pliki gap_report*.md, które wskazują na kod samego narzędzia (cykl samodoskonalenia jest omówiony dalej).
  • finalize_case pełni rolę konsolidatora. Gdy sprawa pozostaje nieużywana przez cztery godziny lub operator wywoła polecenie admin: finalize <case>, gromadzi wszystkie raporty o lukach, usuwa fałszywie pozytywne wyniki i otwiera jeden MR z poprawkami, które przetrwały. Ten MR dotyczy kodbazy harness, a nie oryginalnego repozytorium ticketa – to właśnie jest konkretna realizacja pętli samodoskonalenia.
  • dev-agent jest tym, które wykonuje prace bezpośrednio. W swoim własnym worktree gitowym dedykowanym każdej sprawie edytuje kod, wysyła zmiany, monitoruje proces CI, naprawia go w przypadku awarii i odpowiada na uwagi recenzentów. Działa w trzech trybach: address_review, fix_ci oraz task (oryginalna implementacja).
  • W tym diagramie istnieją dwa szczegóły, które łatwo przeoczyć.

    Najpierw nic nie wpływa na główny proces płatności, ponieważ cała praca odbywa się w worktree’ach. Dzięki temu kilka przypadków może działać równolegle bez konfliktów. Poller oraz dysponent dev-agent umieszczają ścieżkę worktree’a w treści przesyłanej, więc LLM w ogóle nie ma dostępu do głównego procesu płatności.

    Z drugiej strony, case_dir służy jako punkt odniesienia dla wszystkiego. Pliki stanu w katalogu ~/.harness/ (patrz sekcja dotycząca trwałości danych) łączą każdą wątkę w Slacku z konkretnym case_dir, a agent rozwiązuje wszystkie ścieżki względem niego. W rezultacie wszystko inne staje się prostsze: wystarczy przenieść katalog, zaktualizować mapę, a każdy proces pracy będzie działał zgodnie z nią.

    Grafik pokazuje, co jest wykonywane. W następnej sekcji wyjaśniono, jak to działa, czyli jakie mechanizmy przekazują polecenie z Slacka do zakończonego dochodzenia.

    Autonomiczny cykl, krok po kroku

    Jeśli ktoś zapyta, co tak naprawdę robi ten system, najkrótsza i szczera odpowiedź brzmi: operator wpisuje jeden polecenie w Slacku, a od tego momentu uruchamia się zamknięty cykl powtarzającego się rozumowania, samokrytyki i używania narzędzi bez nadzoru, aż praca zostanie zakończona lub system przyzna, że nie może dalej funkcjonować. Niemal każda ciekawa decyzja projektowa kryje się w tym zdaniu, a reszta tej sekcji ją wyjaśnia.

    Wewnątrz tego układu znajdują się cztery odrębne pętle agentów, z których każda jest dostosowana do rozwiązywania swojego rodzaju problemu:

    To wykres pokazuje ich strukturę. Dalej następuje opis treści, przy czym każdy mechanizm jest przedstawiony w kolejności swojego działania. Diagram odnosi się do wszystkich tych elementów, a jego węzły stają się zrozumiałe po przeczytaniu poniższych sekcji. Kody bram, których używa, to G1 do G4, N1 do N3, N5, A1 do A3 oraz D1 do D15.

    Przed pętlą: blokada przed podjęciem decyzji

    Dwie zasady bezpieczeństwa działają przed pierwszym połączeniem z Claude, a pierwsza ma charakter strukturalny. Zanim rozpocznie się jakakolwiek iteracja dochodzenia, proces on-call wymaga od Claude podjęcia decyzji dotyczącej hypothesis_slate. Ten dokument zawiera co najmniej sześć kandydujących wyjaśnień wraz z czterema lub pięcioma kryteriami do ich wyboru, a jego treść jest zapisana w pliku kickoff_precommitment.json, który nie może być później modyfikowany.

    Ze wszystkich elementów w tym systemie to właśnie ten mechanizm zabezpieczający ma największą wagę metodologiczną przy jednoczesnie najniższej widoczności. Nieograniczony cykl rozumowania modeli językowych ma tendencję do przyjmowania tej wersji faktów, którą popierają pierwsze dostępne dowody, a wszystkie późniejsze dowody interpretuje jako potwierdzenie tej decyzji. Ten mechanizm nie może powstrzymać takiego trendu. Zamiast tego zmusza każde dochodzenie do rozpoczęcia się od całego zbioru hipotez oraz do zapisania drogi, którą przebyło od nich do sformułowania wniosku. Następnie recenzent w roli przeciwnika (faza B) stawia trudniejsze pytanie niż to dotyczące spójności wewnętrznej: czy to właśnie taki wniosek przewidziałby pierwotny model, biorąc pod uwagę dostępne dowody?

    Drugim zabezpieczeniem jest empiryczny punkt odniesienia (faza C). Tylko podczas pierwszej iteracji wykonywane są kilka poleceń na poziomie systemu operacyjnego bez udziału LLM w pętli: wyświetlanie ostatnich błędów z journalctl, robienie kopii dysku i pamięci oraz wyszukiwanie plików logów. Wyniki te trafiają do początkowego pytania badawczego jako surowe dowody, dostarczając Claude’owi rzeczywistego obrazu maszyny przed sformułowaniem jakichkolwiek hipotez. Ten krok nigdy nie blokuje procesu, cicho ignoruje błędy i trwa mniej niż sekundę. Zasłużył na swoje miejsce, ponieważ następny krok rozumowania może wtedy opierać się na faktach, a nie tylko na opisie problemu.

    Ustrukturyzowany wynik: raport uzupełnienia

    Każda iteracja kończy się parsowalnym plikiem JSON, completion_report, który narzędzie odczytuje, aby zdecydować, co nastąpi dalej. Jego struktura jest ustalona:

    {
      "status": "IN_PROGRESS" | "BLOCKED" | "COMPLETE",
      "confidence": 78,
      "open_questions": ["..."],
      "unchecked_sources": [{"name": "...", "access_status": "..."}],
      "contradiction_register": ["..."],
      "assumption_register": ["..."],
      "adjacent_problems": [{"summary": "...", "status": "...", "blocker": "..."}],
      "draft_response": "the user-facing Slack message"
    }
    

    Proza w formie swobodnej może otaczać ten blok, ale bramy oceniają zinterpretowany raport, a pliki stanu go przechowują. Zastąpienie części wolności w formie prozy możliwością parsowania kontraktu okazało się najskuteczniejszym sposobem na utrzymanie użyteczności wyników LLM w ramach procesu przetwarzania. Jeśli pominąć schemat, każdy kolejny krok będzie musiał wydobywać fakty bezpośrednio z tekstu prozowego. Jeśli go zachować, bramy stają się zwykłym, deterministycznym kodem w Pythonie.

    Bramy jakościowe, które nadają znaczenie poziomowi pewności

    Punktacja pewności byłaby jedynie hałasem marketingowym, gdyby Claude mógł po prostu wpisać confidence: 95 i przejść dalej. Dlatego wyniki każdej iteracji przechodzą przez bramy jakościowe, które nie pozwalają kontynuować analizy, dopóki dowody i podany poziom pewności się nie zgadzają.

    Po każdej akcji Claude’a uruchamiają się trzy rodzaje bram:

    • G-gates sprawdzają spójność logiczną, aby rozumowanie nie było sprzeczne pomiędzy rundami. Najjaśniejszym przykładem jest G1, który aktywuje się wtedy, gdy liczba open_questions wzrosła podczas rundy, a jednocześnie confidence się zwiększył, ponieważ rosnący mianownik znosi wszelki wzrost tej wartości. Ten mechanizm sprawdza również, czy sformułowania o charakterze napięciowym w tekście są traktowane jako sprzeczności, oraz czy sąsiednie problemy są rejestrowane jako ważne obiekty stanu, a nie tylko jako placeholder’y. Istnieje ich cztery.
    • N-gates sprawdzają kompletność strukturalną, upewniając się, że elementy są w pełni sformułowane i nie stanowią tylko fragmentów. N1 wymaga, aby każda możliwa do wykonania akcja korygująca była połączona z verify_action, który zawiera opis, oczekiwany wynik oraz informacje dotyczące czasu realizacji. Korekta zaproponowana bez możliwości jej weryfikacji jest uważana za prace niezakończone. Istnieje ich trzy.
  • Bramki A narzucają mechaniczne ograniczenia, których proza nie może obejść. A1 oblicza sztywny limit pewności na poziomie 1,0, minus 0,08 za każde otwarte pytanie, minus 0,05 za każde niesprawdzone źródło, a cokolwiek napisze Claude, bramka arytmetycznie dostosowuje tę wartość. Inne bramki A blokują wynik COMPLETE, dopóki sąsiednie problemy pozostają otwarte, wymagają niepustego rejestru założeń i nalegają na to, aby przynajmniej jedna proponowana akcja bezpośrednio odpowiadała przyczynie głównej. Istnieje ich siedem.
  • Nad nimi znajdują się dwa dodatkowe kontrole. EQ1 wymaga, aby węzły przyczyn głównych podawały twarde dowody (cokolwiek innego niż INFERRED) lub akceptowały limit 60%, natomiast P7 obniża ten limit o 7% za każdą wymiarę adwersarialną oznaczoną jako FAIL, aż do minimalnego poziomu 40%.

    Cały ten framework składa się z 19 funkcji. Praktyczny sposób na zapamiętanie ich to: bramki G reagują na to, co pisze Claude, bramki N na to, co pominie, a bramki A na to, co twierdzi.

    Naruszenie staje się wpisem pending_guard_notes, który jest umieszczany na początku promptu w następnej iteracji pod nagłówkiem ## SYSTEM QUALITY GUARD NOTES (z poprzedniej iteracji), wraz z wyraźną instrukcją, by najpierw rozpatrzyć każdą notatkę. To właśnie jest mechanizm przymusu. Bariery nie blokują modelu – sprawiają, że jego wcześniejsze naruszenia stają się pierwszą rzeczą, którą model odczytuje w kolejnej rundzie. Uprzejmość ze strony modelu nie ma znaczenia, a to właśnie strukturalne egzekwowanie napędza postęp w dochodzeniu.

    Krok przytwierdzania kontekstu (A7) jest również wykonywany na końcu każdego budowniczego zapytań, niezależnie od tego, czy doszło do jakichkolwiek naruszeń. Ponownie przypina nierozwiązane sprzeczności oraz niezweryfikowane założenia z bieżącego raportu, słowo w słowo, na dół następnego zapytania. To przeciwdziała dobrze znanemu efektowi słabnięcia uwagi, przy którym model odnotowuje komplikację w trzecim rundzie, a do siódmej rundy już o niej zapomina.

    Pewność jako konkretna liczba

    Najważniejszą decyzją projektową jest to, że agent przypisuje sobie wartość confidence: w skali od 0 do 100 we strukturyzowanym wyniku każdej iteracji. Ta liczba nie służy jedynie dekoracyjnemu celowi, ponieważ kontroluje rzeczywiste zachowanie:

    • Gdy wartość jest poniżej 70, nie można przerwać pętli. Proces zmusza do przeprowadzenia jeszcze maksymalnie trzech rund oraz dodaje notatkę ostrzegawczą, w której stwierdza się, że agent twierdził, iż skończył pracę, mimo że wartość była poniżej wymaganego progu.
  • Gdy wartość wynosi 70 lub więcej, przy status: COMPLETE i braku open_questions, otwiera się droga do fazy recenzji adwersarialnej (zespół czerwony przeciwko zespołowi niebieskiemu).
  • Gdy wartość wynosi 95 lub więcej, agent może automatycznie wykonać działania nieniszczące, takie jak komentarz w Jira, zmianę statusu lub przydzielenie ticketa. Poniżej 95 pozostają one w formie szkiców do decyzji operatora.
  • Spadek o ponad 10% w dwóch kolejnych rundach kończy pętlę z powodem degrading. Agent cofa się, więc zatrzymuje działanie i informuje o tym operatora.
  • Pełna ścieżka pewności każdego przypadku jest zapisywana w pliku ANALYSIS.md w postaci wykresu liniowego, na przykład 45% → 62% → 78% → 80%. Operatorzy sprawdzają ją, aby ocenić, czy wynik zasługuje na zaufanie, a sam mechanizm doskonalenia się czyta ją podczas wyboru przypadków z przeszłości do ponownego przetworzenia z innym promptem.

    Trzy sposoby, w jakie pętla odmawia wcześniejszego zakończenia

    Na diagramie trzy gałęzie dochodzą do stanu ready_for_adversarial lub blocked, bez kończenia pętli. Każda z nich stanowi regułę przymusowego kontynuowania: system ignoruje sygnał wyjścia i zamiast niego wprowadza notatkę ochronną.

    • REGUŁA_CONFIDENCE_FORCE zostaje zastosowana, gdy status=COMPLETE i open_questions=[], ale wartość confidence jest poniżej 70. Model ogłosił zwycięstwo, jednocześnie dając do zrozumienia wątpliwości poprzez tę wartość. Przymusowo wykonuje się jeszcze do 3 iteracji, które są liczone w ramach limitu 20 iteracji, zamiast go powiększać.
  • CAUSAL_CHAIN_FORCE jest stosowany, gdy status=COMPLETE i confidence wynosi co najmniej 70, ale causal_chain_complete nie jest prawdziwe. Jest to stwierdzenie na poziomie objawu bez zidentyfikowanej przyczyny źródłowej. Zmusza do dodatkowych maksymalnie 3 iteracji, wciąż w ramach limitu 20 iteracji.
  • G_BLOCKER_FORCE jest stosowany, gdy status=BLOCKED, a dostępne źródła techniczne zostały pominięte lub uznane za niedostępne na podstawie wnioskowania, a nie z powodu rzeczywistego błędu. Badanie nie może zakończyć się jako zablokowane, zanim faktycznie nie wykorzysta wszystkich dostępnych możliwości. Pozwala na maksymalnie 3 kontynuacje, również w ramach tego samego limitu.
  • Wspólnie zapobiegają one trzem najczęstszym przedwczesnym zakończeniom: niepewności wydającej się pewna, zakończeniu na poziomie objawu oraz domniemanej niedostępności.

    Adwersarialny recenzent

    Gdy pipeline do pracy w trybie awaryjnym zgłosi ukończenie pracy przy poziomie pewności 70 lub wyższym, kontrola przechodzi na osobną inicjację Claude pełniącą rolę krytyka kontrargumentującego. Ten krytyk nie widzi zapytania zadanego przez badacza. Otrzymuje tekst analizy oraz listę kontrolną zawierającą 15 wymiarów oceny, od D1 do D15, obejmujących ważność założeń, łańcuch dowodów, rozumowanie przyczynowo-skutkowe, alternatywne hipotezy i inne aspekty. Zwraca on ustrukturyzowaną decyzję:

    Istnieją trzy możliwe rozwiązania po zakończeniu oceny:

    1. Krytyczne problemy są zwracane badaczowi. Jego notatki są włączane do następnej iteracji jako pending_guard_notes, a pętla restartowana, przy czym badacz otrzymuje jasne potwierdzenie, że krytyk uważa, iż coś zostało przeoczone.
    2. Jeśli przy pierwszej próbie występują jedynie drobne problemy, pętla jest uruchamiana jeszcze raz z dołączoną drobną krytyką.
  • Dwa kolejne przejścia obejmujące wyłącznie zadania niższej trudności lub jedno przejście bez błędów zakończą pętlę wartością complete, a wyniki dochodzenia zostaną opublikowane.
  • Decyzja o przeprowadzeniu kroku adwersarialnego jako odrębnej próby z innym promptem jest celowa. Ten sam model w innej roli potrafi wykryć znaczną część samochwalstwa, które przechodzi w przypadku iteracji z jednym promptem. W jednym przypadku poziom pewności siebie spadł z 90 na 65 pomiędzy przeprowadzeniem dochodzenia a jego przeglądem, przy czym recenzent wskazał właśnie na założenie, które badacz zignorował.

    Razem z tym odbywa się druga ocena, opracowana w inny sposób: ocena zespołu czerwonego (G5). Recenzent w roli przeciwnika czyta pełny opis dochodzenia, natomiast recenzent zespołu czerwonego otrzymuje jedynie początkowe sformułowanie problemu oraz końcowe wnioski. Nie ma dla niego żadnych pośrednich rozumowań, żadnej ścieżki dowodowej ani wcześniejszych etapów. Na podstawie tych dwóch elementów formułuje on niezależną hipotezę i zaznacza miejsca, w których ustalenia dochodzenia odbiegają od tego, co przewidziałby osoba z zewnątrz.

    Celem jest izolacja poznawcza, a nie dodatkowe przykrycie. Recenzja typu adversarial sprawdza, czy dochodzenie jest strukturalnie kompletne (od D1 do D15). Zespół red team pyta, czy do wniosku można było dojść bez określonego ujęcia dochodzenia: czy rozumowanie rzeczywiście opiera się na problemie, czy też dochodzenie oparło się na wczesnym ustaleniu i pozwoliło, by wniosek wynikał z tego ustalenia zamiast z dowodów? Obie procedury są przeprowadzane jako oddzielne wywołania Claude’a i obie muszą się udać, zanim sprawa zostanie oznaczona jako COMPLETE.

    Pięć uczciwych sposobów zakończenia

    Pętla dochodzenia może się zakończyć z pięciu powodów. Każdy z nich jest rejestrowany i pokazywany operatorowi, jak w poniższych przykładach:

    exit=complete       confidence=89%  iterations=12  elapsed=13min
    exit=blocked        confidence=62%  iterations=4   elapsed=4min    (cannot proceed without info)
    exit=stalled        confidence=58%  iterations=7   elapsed=22min   (two rounds, zero new facts)
    exit=degrading      confidence=42%  iterations=5   elapsed=8min    (dropped >15% twice)
    exit=timeout        confidence=73%  iterations=18  elapsed=50min
    

    Ze wszystkich pięciu blocked to ten, na który operator może oddziaływać w najbardziej bezpośredni sposób. Agent zapisuje wyraźne blokery w unchecked_sources lub adjacent_problems, a człowiek musi wykonać połączenie, aby je usunąć. Pozostałe cztery to tryby awarii technicznych, które system wykrywa i nie próbuje automatycznie ponownie.

    Co Claude może uzyskać od wewnątrz

    Tutaj istotne są granice narzędzi, ponieważ progi pewności częściowo zależą od tego, czego Claude faktycznie może użyć. Zasada nakazująca agentowi sprawdzenie niesprawdzonych źródeł nie ma sensu, jeśli nie istnieją narzędzia do ich sprawdzenia. Każdy proces uruchamia podproces Claude Code z parametrem --permission-mode bypassPermissions. W zapytaniu nie pojawia się lista dozwolonych narzędzi. Granicą jest to, co jest zintegrowane z katalogiem Model Context Protocol (MCP), który obejmuje pięć zewnętrznych systemów:

    Należy wspomnieć o kilku ograniczeniach, ponieważ wpływają one na zachowanie modelu bardziej niż cokolwiek innego podane na karcie modelu:

    • Proces badawczy wyraźnie zabrania użycia Bash (--disallowedTools Bash), co zapobiega sytuacjom rywalizacji w git, gdyż dwa procesy Claude edytujące jednocześnie ten sam drzewo robocze mogłyby uszkodzić stan danych. Proces dev-agent faktycznie używa Bash, ale działa on w izolowanym drzewie roboczym.
    • Narzędzia do zapisu w Slack są blokowane przy każdej wywołaniu procesu. Funkcje send_message_to_self i edit_message_to_self są zabronione, więc Claude nie może samodzielnie publikować treści w Slack. Cała komunikacja z zewnątrz przechodzi przez pole draft_response w strukturalnym raporcie uzupełnień, który system wysyła dopiero po weryfikacji warunków zakończenia.
  • bypassPermissions w praktyce nie oznacza braku ograniczeń. Każdy prompt używany w procesie zawiera ustalone zasady. Plik ANALYSIS.md można edytować, natomiast plik message.json nie. Zapisy poza katalogiem case_dir są automatycznie cofane za pomocą git diff po zakończeniu procesu. Zapisy do bazy danych produkcyjnej wymagają wyraźnej potwierdzenia operatora zgodnie z poleceniem. Bezpieczeństwo zapewnia warstwa formułowania zapytań oraz sprawdzenie granic po wykonaniu zadania, a nie warstwa wywoływania narzędzi.
  • Rozwiązania awaryjne są zawarte w promptach i nie są zapisane w sposób sztywny. Jeśli funkcja search_datadog_services zwróci błąd braku uprawnień, prompt kieruje agenta do użycia opcji mcp__glean_default__search, wykorzystując słowa kluczowe z Jira jako zamiennik. Ta łańcuchowa struktura rozwiązań awaryjnych stanowi element realizacji procedur standardowych i nie ma miejsca w czasie wykonywania zadań.
  • Narzędzia do odczytu działają równolegle, natomiast narzędzia do zapisu – sekwencyjnie. Proces potomny w trybie strumieniowym analizuje zdarzenia tool_use w momencie ich przybycia i uruchamia do 10 jednoczesnych operacji odczytu (zapytania Datadog, wyszukiwania w Glean, odczyty plików). Wszystko, co polega na zapisie – czy to Bash z efektami ubocznymi, edycje, komentarze MR czy zapisy w bazie danych – odbywa się po kolei. Dlatego jedna iteracja dochodzenia może uruchomić sześć równoległych zapytań do logów i nadal utrzymać uporządkowanie operacji zapisu.
  • Jeden szkielet, cztery rozmiary

    Każdy pipeline w tym zestawie wykorzystuje jakąś wersję tego samego szkieletu:

    Proces dochodzeniowy w trybie on-call przeprowadza maksymalnie 20 iteracji. Faza analizy zadań obejmuje do 8 iteracji. Faza wdrażania uruchamia szkielet tylko raz, ale powtarza krok budowania trzy razy. Dev-agent nie ma wewnętrznego pętli i działa raz na każde uruchomienie, natomiast mr-monitor faktycznie przenosi iterację na zewnątrz: uruchamia nowego dev-agenta za każdym razem, gdy CI zawodzi, maksymalnie trzy razy z rzędu, zanim przekaże sprawę człowiekowi.

    Jednolitość jest celowa. Te cztery pętle istnieją, ponieważ problemy się różnią, ale używają tego samego języka: poziom pewności to liczba, zakończenie ma nazwę, samokrytyka to odrębne wezwanie do Claude’a, a eskalacja oznacza wiadomość prywatną w Slacku w temacie, który rozpoczął sprawę. Operator uczy się tej struktury raz i potem rozpoznaje ją wszędzie.

    Szczegół dotyczący samodoskonalenia

    To zamyka wcześniej opisany cykl, w którym narzędzie modyfikuje własną bazę danych pod nadzorem człowieka. Praca jest wykonywana w dwóch cyklach, na różnych skalach czasowych.

    Cykl 1 naprawia strukturę przypadek po przypadku, od luk do propozycji zmian. Po zakończeniu dochodzenia gap_analyzer szuka miejsc, w których narzędzie nie funkcjonowało prawidłowo: pominięte źródła danych, błędne wybory narzędzi, błędy w procesie przetwarzania, brakujące testy oraz źle sformatowane opisy propozycji zmian. Filtry kategorii i poziomu pewności redukują liczbę tych luk, zanim trafią one do finalize_case. Gdy dany przypadek leży bezczynnie przez cztery godziny, finalize_case uruchamia patch_suggester w dedykowanym drzewie prac i tworzy jedną propozycję zmian wobec własnej bazy kodu narzędzia, wpływając na pliki z kodem, szablony komunikatów lub sekcje procedur operacyjnych. Połączenie tej propozycji wymaga interwencji człowieka, natomiast jej napisanie nie.

    Pętla 2 wzmacnia zachowanie w różnych przypadkach, od trybów awarii po sugestie dotyczące procedur operacyjnych. Na końcu każdego przypadku krok auto_retrospective rejestruje, jakie tryby awarii wystąpiły, sortowane na trzy kategorie: błędy w zbieraniu danych (czy pominięto dostępne źródła?), naruszenia standardów jakości (czy twierdzenie o pewności kłóciło się z dowodami?) oraz błędy w kompletności analizy (czy śledzenie przyczyn podstawowych zostało przerwane?). Rejestracje gromadzą się w rejestrze przypadków dla każdego projektu. Co tydzień zadanie cron o nazwie knowledge-updater liczy ostatnie dziesięć przypadków i, gdy jakiś tryb przekroczy określony próg, tworzy projekt sugestii procedury operacyjnej: 3 z 10 przypadków skutkują sugestią, a 6 z 10 – alertem. Sugestie są konkretne. Na przykład „pominięcie bezpośredniego zapytania do bazy danych” wystąpiło 3 razy w tym miesiącu, więc procedura operacyjna wymaga teraz bezpośredniego zapytania do tabeli przed jakąkolwiek inferencją dotyczącą schematu. Ludzie przeglądają je i je wdrażają.

    Em, i nic nie jest tutaj automatycznie stosowane zgodnie z projektem, ponieważ zautomatyzowane decyzje, które powielają własne błędy, stanowią znane zagrożenie w systemach samomodificujących się.

    Z tego wynika personalizacja. SOP zawiera obraz tego, w jaki sposób zespół prowadzi dochodzenia, i na początku ma charakter ogólny. Z biegiem czasu kb-updater tworzy profil wzorców niepowodzeń danego projektu na podstawie poszczególnych przypadków i powtórzeń. Po 20 do 30 przypadkach SOP tego projektu przestaje przypominać szablon ogólny: odnotowuje, które źródła danych są ciągle pomijane, jakie narzędzia są niewłaściwie używane oraz jakie metody dochodzeniowe regularnie zawodzą w tym konkretnym kodzie. Projekty posiadają oddzielne SOP-y, więc błędy zespołu A wpływają tylko na zasady zespołu A i nigdy nie zakłócają prac zespołu B.

    Kolejną zaletą jest trwałość. Okno kontekstowe istnieje tylko podczas jednej sesji, natomiast zmiana kodu po połączeniu jest wersjonowana, sprawdzana, testowana i trwała. Aktualizacja SOP, po jej zatwierdzeniu przez operatora, pojawia się we wszystkich kolejnych dochodzeniach. Podstawa systemu stale się udoskonala, dzięki czemu każda nowa sprawa jest obsługiwana za pomocą lepszego rozwiązania niż poprzednia.

    Szczegół 3: stan przetrwający restarty

    Orchestracja to mechanizm działający przy każdym zdarzeniu. Stan trwały to informacje, które pozostają po restartowaniu procesu, ponownym uruchomieniu maszyny lub przeniesieniu operatora z laptopa na wirtualną maszynę chmurową. Rozwiązanie to przechowuje swój stan w trzech warstwach o zupełnie różnych właściwościach:

    Pamięci cache w pamięci operacyjnej są małe i można je odbudować. Znajdują się w procesie monitorowania i utrzymywania połączenia oraz są odbudowywane na podstawie lokalnego stanu po restartowaniu.

    Stan lokalny w plikach ~/.harness/*.json stanowi mapę operacyjną. Kluczowe pliki to:

    • task-thread-map.json łączy wartość thread_ts z Slacka z identyfikatorem JIRA oraz bezwzględną ścieżką task_dir.
    • oncall_thread_map.json ma taką samą strukturę dla przypadków dochodzeń w trybie on-call.
    • monitored-mrs.json wymienia MR-y otwarte przez harness wraz z ich stanem w CI.
    • patch-pr-state.json rejestruje stan zakończenia każdego przypadku (merged, no_gaps, failed itp.).
    • oncall-state.json zawiera flagę kill-switch, omówioną w warstwie 6.
    • socket-dedup przechowuje daty czasowe ostatnich wiadomości już przetworzonych w trybie Socket.

    Ta warstwa należy do jednej maszyny i nie jest udostępniana. Przetrwa restart, ale jeśli maszyna przestanie działać, zostanie usunięta.

    Stan zsynchronizowany z Gitem jest warstwą trwałą. Przestrzenie robocze przypadków znajdujące się pod adresem case-workspaces// są zapisywane i wysyłane do prywatnego repozytorium Git przy każdej istotnej zmianie stanu. Druga maszyna, która klonuje to repozytorium, może przejąć ten sam kontekst rozmowy, co zapewnia ciągłość sesji między maszynami. Operator może rozmawiać z systemem z laptopa lub zdalnej maszyny wirtualnej deweloperskiej bez żadnych strat.

    Konsolidacja pamięci stała się później trzecim zadaniem dla stanu synchronizowanego przez git-sync. Długa rozmowa generuje dziesiątki plików followup_transcript_*.md. Gdy nagromadzi się wystarczająco dużo aktywności, podagent czyta je wszystkie, przepisuje TASK.md, aby zawierał skondensowany stan, a proces przestaje wprowadzać surowe transkrypty w kolejnych etapach. Pomijanie konsolidacji może sprawić, że prompty przekroczą rozmiar okna kontekstowego modelu. Jej wykonywanie niesie ryzyko zbyt silnego skompresowania i utraty informacji, co rzeczywiście się zdarzyło: dokument o rozmiarze 14 KB skurczył się do 587 bajtów. Z tego incydentu powstały trzy zabezpieczenia: szablon promptu z napisem „UPDATE, a nie REWRITE”, mechanizm kontrolujący rozmiar wyjścia, który odrzuca zapisy poniżej 50% rozmiaru wejścia, oraz plik blokujący dla poszczególnych przypadków, aby dwie rundy konsolidacji nie mogły się nakładać.

    Droga wyszukiwania od pojedynczej wątku w Slack do pliku TASK.md danego przypadku jest krótka, ale warto ją przedstawić:

    Oba pliki stanu przechowują ten sam rodzaj wartości – absolutną ścieżkę do przestrzeni roboczej przypadku. Każdy proces pracy prędzej czy później trafia do case_dir, a następnie działa względem niego. Gdy w trakcie projektu zmieniono nazwę korzenia przestrzeni roboczej, jeden skrypt migracyjny poradził sobie z tym poprzez przepisanie absolutnych ścieżek w obu mapach jednocześnie z przeniesieniem katalogu.

    Trwały stan stanowi podstawę. Następnym pytaniem jest to, co się dzieje, gdy operacje oparte na nim zawodzą, a odpowiedź brzmi, że system sam się z tego odbudowuje i nie przekazuje tych problemów operatorowi.

    Szczegół 4: pętle, które same się naprawiają

    To właśnie w tym szczególe system przestaje pełnić rolę powiadamiacza i staje się autonomiczny. Zawiera on trzy pętle, z których każda jest domknięta bez udziału operatora.

    Pierwsze dwa są w pełni autonomiczne. Trzeci celowo nie jest. Odświeżenie sesji dostawcy tożsamości wymaga prośby o MFA związanej z sprzętem, więc agent się zatrzymuje i prosi o interwencję człowieka, co powoduje przerwę trwającą 30 sekund. To jest zaprojektowane tak, by człowiek brał udział w procesie, i nie ma tu żadnej luki w możliwościach.

    Te pętle mają jedną subtelną cechę wspólną: każda z nich ma limit prób i potrafi rozpoznać, co już zostało zrobione. Automatyczne naprawy w CI są ograniczone na poziomie każdego MR, więc trzy kolejne niepowodzenia skutkują interwencją operatora. Automatyczne odpowiedzi recenzentów są unikalne pod względem autora, co oznacza, że gdy najnowsza notatka bota w dyskusji jest nowsza od notatki recenzenta, pętla pozostaje bezczynna, ponieważ odpowiedź już istnieje. Narzędzie do zarządzania dostawcami tożsamości umożliwia 15-minutowe okno na zatwierdzenie. Usunięcie tych ograniczeń czasami powoduje eskalację sytuacji w tych pętlach. Na początku jeden MR z niestabilnym testem integracyjnym przyjął 80 komitetów typu „naprawa CI”, które w rzeczywistości nic nie zmieniały, w ciągu jednego popołudnia, zanim ktokolwiek to zauważył.

    Odzyskiwanie po pojedynczych operacjach

    Powyżej opisane pętle automatycznego naprawiania w CI oraz odpowiadania przez recenzentów umożliwiają przywrócenie całych procesów od początku do końca. Poniżej nich każda operacja podstawowa, która może się nie udać (push, rebase, komentarz w MR, sformatowanie gałęzi), opiera się na odrębnym, mniejszym modelu trzech warstw służącym do samodzielnego przywracania stanu po każdej operacji. Różni się on od modelu bezpieczeństwa opartego na trzech warstwach z Warstwy 6, który dotyczy ograniczeń, czyli tego, co agent może robić. Ten model natomiast skupia się na przywracaniu stanu, czyli na tym, co dzieje się, gdy dozwolona operacja zawodzi. Łatwo je pomylić, więc trzymaj je oddzielnie.

    L1 to deterministyczny mechanizm ochronny. Działa na podstawie reguł i jest zawsze aktywny, reagując w czasie krótszym niż sekunda. W przypadku operacji push używa się _pre_push_rebase(repo_path, branch): tuż przed wykonaniem pusha narzędzie uruchamia git fetch origin oraz git rebase origin/. Udane rebase umożliwia dalsze wykonanie operacji push. Konflikt lub nieudane pobranie sprawiają, że L1 bezpiecznie przerywa działanie, wyświetla pliki w konflikcie i przenosi tę sytuację do L2. L1 nie może celowo być kreatywny – reguły są na tyle proste, by można je było zrozumieć, a ich koszt jest na tyle niski, że nie zużywają żadnych zasobów, dzięki czemu mogą być stosowane do każdej operacji.

    L2 to pętla samoregeneracyjna oparta na mechanizmie agenta. Gdy L1 nie jest w stanie naprawić sytuacji, system uruchamia ograniczoną sesję Claude (_self_heal_push_loop) i przekazuje jej wszystkie informacje na temat awarii: stan plików git, sprzeczne pliki, zadanie, które skłoniło do wykonania operacji push, oraz ważne ograniczenia. Nigdy nie należy wykonywać forced push na chronionym gałęzi, nigdy nie należy odrzucać niewysłanych komitetów i nigdy nie należy ślepo wybierać jednej ze stron konfliktu. Trzy takie ograniczenia zapewniają bezpieczeństwo:

    1. Ograniczone próby ponownego działania. Każdy incydent daje maksymalnie 3 próby, a każda tura Claude ma czas timeout wynoszący 4 minuty. Po każdej akcji Claude narzędzie ponownie wykonywa rzeczywiste polecenie git push. Opinia Claude na temat zniknięcia konfliktu nie ma żadnego znaczenia – jedynym wiarygodnym wskaźnikiem jest kod wyjścia 0 z polecenia git push. Jeśli kolejne polecenie zwróci wartość inną niż 0, pętla rejestruje nieudaną próbę, bez względu na to, jak pewnie Claude twierdził o sukcesie.
    2. Brama pewności na poziomie 70 lub wyższym. Została ona bez zmian zapożyczona z konfiguracji oncall_run w pipeline’u dochodzeniowym. Claude ocenia sam siebie od 0 do 100 punktów w każdej turze – jeśli wynik jest poniżej 70, musi się wycofać zamiast podejmować działania. Ocena poniżej minimalnego progu nie daje mu prawa do edycji drzewa roboczego.
  • Inwariancka prawdziwość jest zachowana. Udane wykonanie kroku L2 oznacza, że rzeczywiście doszło do zapisania zmian w serwerze origin. Brama odpowiedzi znajdująca się dalej w łańcuchu, która wysyła recenzentowi komunikat „Gotowe, zaktualizowane”, pozostaje bezpośrednio połączona z wynikiem git push równym 0 i nigdy nie jest powiązana z niczym, co Claude mówi o sobie. L2 nie może ominąć tej bramy – może jej dostarczyć jedynie rzeczywisty polecenie push.
  • L3 to eskalacja do operatora. Jeśli L2 zrezygnował z dalszej pracy z powodu niskiego poziomu pewności lub wyczerpał swoje próby, system wysyła do operatora prywatną wiadomość zawierającą całą historię działań L2, czyli każdą próbę, każdą akcję podjętą przez Claude’a, każdy oceniony poziom pewności oraz wynik polecenia git status. Operator rozpoczyna pracę od w pełni zinstrumentalizowanego stanu, a nie od informacji o błędzie. Skala czasowa wynosi godziny, a nie sekundy.

    Wyróżniają się dwa zasady:

    • L2 jest aktywny tylko w przypadku konkretnego incydentu i nie działa stale. L1 uruchamia się przy każdym pushu, ponieważ jest tani, natomiast L2 zużywa tokeny i zostaje uruchomiony dopiero po niepowodzeniu L1, więc koszt jest uzależniony od rzadkich przypadków, a nie od tych częstych.
    • Prawdziwa wartość pochodzi bezpośrednio z systemu. Bez względu na to, co Claude uważa o swojej pracy, proces walidacji powtarza rzeczywistą operację: kod wyjścia git push dla operacji push, rzeczywiste przetestowanie poprawek oraz sprawdzenie kodu wyjścia push w przypadku odpowiedzi na propozycje zmian. Samodzielne raporty agenta nigdy nie są traktowane jako prawdziwa wartość, ponieważ system ponownie sprawdza je w odniesieniu do rzeczywistego systemu.

    To samo otoczenie nadaje się do znacznie więcej niż tylko do wykonywania operacji push. Operacja spełnia kryteria, gdy istnieją dla niej trzy elementy: prosta korekta oparta na regułach (L1), ściśle ograniczona próba ponownego wykonania przez agenta (L2) oraz prosty sposób przekazania zadania osobie (L3). Ta trójkondycyjna struktura odróżnia autonomiczny cykl, który potrafi samodzielnie radzić sobie z błędami, od takiego, który wymaga interwencji człowieka już w momencie, gdy sytuacja ulega zmianie.

    Szaryt 5: cztery miejsca, gdzie można zobaczyć, co się stało

    Narzędzie to zapisuje dane na czterech platformach umożliwiających obserwację, a każda z nich jest dostosowana do innego odbiorcy:

    • Łączne aktualizacje w Slacku. Podczas pracy agenta wiadomość tymczasowa w wątku sprawy przechodzi przez różne stany (Reading TASK.md, następnie Searching codebase, potem Drafting patch) i ostatecznie zmienia się na Done lub Failed: <reason>. Ten strumieniowy raporter edytuje jedną wiadomość bezpośrednio, dzięki czemu wątki nie wypełniają się zbędnymi informacjami.
    • Zstrukturyzowane logi znajdują się w plikach ~/.harness/logs/agent.log oraz ~/.harness/logs/error.log. Każde uruchomienie, przekierowanie łańcucha oraz próba ponownej realizacji stają się zstrukturyzowanymi zdarzeniami, a to właśnie te pliki są sprawdzane, gdy konieczna jest diagnostyka.
  • Wiadomości prywatne w Slacku dotyczące zdarzeń terminalowych, takich jak otwarcie MR, połączenie MR, wygasła sesja dostawcy tożsamości lub eskalacja w CI. Są one traktowane jako powiadomienia pierwszej klasy dla operatorów i umieszczane w temacie odpowiedniego przypadku.
  • Log systemowy w lokalizacji ~/.harness/logs/system.log dla zdarzeń ogólnych, takich jak zakończenie deploju, działania watchdoga oraz status synchronizacji.
  • Panel sterowania został celowo pominęty. Slack i grep pełnią rolę konsol. Panel sterowania stanowiłby trzecią powierzchnię do śledzenia informacji, a jest to pierwsza rzecz, której inżynierowie przestają czytać, gdy staje się przestarzały. Wzorzec wiadomości strumieniowych dostarcza wystarczająco dużo sygnałów operacyjnych, dlatego nikt nie chciał dodawać nad nim interfejsu graficznego.

    Nadzór połączeń MCP

    Pięć zewnętrznych serwerów MCP (Slack, Datadog, Jira i Confluence, Glean, GitLab) stanowi podstawę tego rozwiązania. Każdy z nich posiada własny token OAuth, określony cykl odnowienia oraz niejasne mechanizmy radzenia sobie z błędami. Gdy token wygaśnie bez żadnego zauważenia, sesja Claude zaczyna odpowiadać „Nie mam dostępu do tego narzędzia”, a operator w ogóle nie widzi żadnego błędu.

    Niewielki serwis o nazwie mcp-watchdog zamienia tę lukę w bezpieczeństwo. Co 10 minut sprawdza punkt końcowy stanu każdego serwera MCP i próbuje wykonać bezskuteczną próbę wezwania narzędzia. Gdy to się nie udaje, podejmuje następujące kroki:

    1. Próbuje cichego odnowienia tokena.
    2. Jeśli odnowienie się nie powiedzie, wysyła do operatora wiadomość prywatną w Slacku w stylu „Atlassian MCP wymaga ponownego uwierzytelnienia, kliknij tutaj.”
    3. Po dwóch kolejnych przerwach w połączeniu (DISCONNECT_CONFIRM_THRESHOLD) eskaluje sytuację, dodając reakcję :warning: do najnowszego zgłoszenia.

    Bez tego pętla błędów sprawia, że wygasły token jest mylony z „agent dzisiaj nie funkcjonuje prawidłowo”, co stanowi trudny do zdiagnozowania problem, ponieważ plik agent.log nigdy nie informuje o „wygasłym tokenie”. Zamiast tego podaje, że „narzędzie zwróciło pusty wynik”. Traktowanie stanu autoryzacji jako kluczowego elementu do monitorowania powinno być wdrożone już wcześniej.

    Szósty warstwa: interfejs sterowania operatora

    To najczęściej używany interfejs, który początkowo nie został wystarczająco rozwinięty. Każda interakcja z operatorem odbywa się przez jeden kanał – self-DM – a polecenia dzielą się na sześć kategorii:

    • Zadanie: admin: task <jira> uruchamia cały proces analizy, wdrożenia i zgłoszenia błędu, a dodanie parametru --auto sprawia, że proces przebiega bez nadzoru.
  • On-call: admin: oncall-run <url> rozpoczyna dochodzenie w temacie Slacka, natomiast oncall-toggle włącza lub wyłącza automatyczny monitoring.
  • Kontrola MR: admin: mr rescan ponownie sprawdza konkretny MR, a mr pause/resume włącza lub wyłącza pętlę automatycznych odpowiedzi dla danego MR.
  • Pauza: admin: oncall pause [duration] tymczasowo wycisza automatyczne przekierowywanie, a oncall resume/status kończy pauzę lub podaje aktualny stan sytuacji.
  • Zatwierdzenie: admin: finalize <case> łączy raporty dotyczące braków i tworzy propozycję ulepszeń skierowaną do samego narzędzia.
  • Księga rejestru: admin: register-thread ręcznie łączy wątek w Slacku z konkretnym przypadkiem, natomiast close-case oznacza go jako zakończony.
  • Najdłużej zajęło ustawienie poprawnego mechanizmu wstrzymania działania. Wersja pierwsza wstrzymywała wszystko, łącznie z poleceniami administracyjnymi, co sprawiało, że operator, który wstrzymał system, nie mógł go wznowić. Wersja druga wstrzymywała działanie zbyt selektywnie, więc prośba o „zatrzymanie hałasu” nie była realizowana. Obecny projekt opiera się na jednej precyzyjnej zasadzie: wstrzymanie działania powoduje uciszenie automatycznego rozdzielania zadań wywołanego aktywnością innych osób, ale nigdy własnych polecenia administracyjnych lub działań operatora w ramach wątku. W systemie poller, miejsca rozdzielania zadań dzielą się na dwie grupy. Jedna grupa to aktywność automatyczna wywołana przez innych i jest wstrzymywana podczas pauzy. Druga to intencja samego operatora, która pozostaje aktywna.

    Kalibracja przyniosła wiele trudności. Krótko po wysłaniu pierwszej wersji z funkcją pauzy, automatyczna odpowiedź na komentarze recenzentów uruchomiła się bezgłośnie w innym przypadku i pozostała niezauważona, ponieważ wydawało się, że funkcja pauzy jest aktywna. Obecny projekt traktuje pauzę jako celowe zamilknięcie: system ucichają, zachowując jednocześnie możliwość działania operatora.

    Zdjęcie pokazuje tę strukturę:

    Zasada jest taka, że własne działania operatora nigdy nie są tłumione, natomiast aktywność innych osób jest ograniczana podczas pauzy. To właśnie kontroluje polecenie admin: oncall pause.

    Dwa dodatkowe warstwy bezpieczeństwa

    Przycisk wyłączający pauzę jest najbardziej widoczną formą zabezpieczenia, choć nie jedyną. Dwie inne działają bez żadnej konfiguracji w czasie uruchomienia – jedna poprzez kod, a druga dzięki konwencjom implementacji:

    Lista dopuszczalnych kanałów w Slacku zasługuje na większe znaczenie, niż się wydaje. Wcześnie w trakcie projektu doszło do niemal incydentu spowodowanego błędnie skonfigurowanym tokenem Slacka, który mógł pisać w kanałach wewnętrznych zespołu. Agent mógłby bezpośrednio publikować wiadomości do kolegów z zespołu, omijając operatora, a nikt nie byłby w stanie odróżnić tych postów od tych wysłanych przez człowieka. Obecnie każda wiadomość wysyłana za pomocą chat.postMessage jest sprawdzana pod kątem celu przed wysłaniem, korzystając z listy dopuszczalnych adresatów. Jedynym dozwolonym celem jest prywatna wiadomość do operatora oraz jeden sztywno zdefiniowany identyfikator kanału przeznaczony na rozmowy operatora z botem. Jeśli agent chce skontaktować się z kolegą z zespołu, przekazuje treść operatorowi, który decyduje, czy ją przesłać. Ta zasada architektoniczna ciągle się sprawdza – agent może komunikować się na zewnątrz wyłącznie przez skrzynkę pocztową operatora.

    Poza sześcioma warstwami: co czyni to rozwiązanie gotowym do użycia w produkcji

    Sześć warstw stanowi konstrukcję nośną. Istnieje kilka mechanizmów, które wykraczają poza tę klasyfikację, ale są równie ważne. One odróżniają system, który dobrze funkcjonuje podczas demonstracji, od tego, który działa bez przerw.

    Kierowanie kilkoma projektami z jednego pliku YAML

    Dziś w tym systemie działają dwa projekty, z których każdy ma własne reguły operacyjne, a dodanie trzeciego nie wymaga żadnych zmian w kodzie – wystarczy jedynie nowy wpis w pliku agent-config/projects.yaml:

    projects:
      team-a:
        sop: "team-a/SOP.md"
        kb: "team-a/KNOWLEDGE-BASE.md"
        dfr_threads_dir: "team-a/threads"
        task_dir: "task-workspaces"
        slack_channels: ["support-channel-a", "support-channel-b"]
        jira_prefixes: ["AAA", "BBB", "CCC"]
      team-b:
        sop: "team-b/SOP.md"
        kb: "team-b/knowledge-base/kb.md"
        dfr_threads_dir: "team-b/threads"
        task_dir: "task-workspaces"
        slack_channels: []
        jira_prefixes: ["XX", "YY"]
    

    Funkcja resolve_project() kieruje każdy przychodzący sygnał według priorytetu: najpierw prefiks Jira, potem kanał Slacka, a na końcu wartość domyślna. Każdy projekt ma swój własny plik SOP, plik KB oraz drzewo katalogów, a mechanizm szybkiego tworzenia zapytań do badań automatycznie wybiera odpowiedni plik SOP i KB w zależności od projektu, do którego odnosi się sygnał. Dzięki temu jedna instancja harness może obsługiwać dwa zespoły z dwoma zestawami reguł.

    To rozwiązanie jest uniwersalne. Każda organizacja posiadająca kilka zespołów produkcyjnych w ramach jednego harness może stosować ten sam podejście: konfigurację deklaratywną bez oddzielnych gałęzi kodu dla poszczególnych projektów. Ponadto zapewnia ono czystą dyscyplinę, ponieważ wszystko, co jest specyficzne dla danego projektu, musi znajdować się w jego pliku SOP, a nie w kodzie, ze względu na to, że kod jest współdzielony.

    Przywracanie sesji na dowolnym komputerze

    Operator pracuje na dwóch urządzeniach: laptopie oraz długowiecznej maszynie w chmurze w centrum danych, a jedna sesja Claude musi być możliwa do wznowienia na obu. Jest to trudne, ponieważ Claude Code domyślnie przechowuje stan sesji na dysku lokalnym, więc po ponownym uruchomieniu w innym miejscu sesja nie jest widoczna.

    Rozwiązanie to polega na użyciu magazynu sesji opartego na git:

    Mechanizm składa się z trzech elementów:

    1. Sesje są śledzone w git. Pliki .jsonl oraz .meta każdej sesji są zapisywane i wysyłane do prywatnego repozytorium przy każdej istotnej zmianie stanu, a to repozytorium stanowi źródło prawdy.
    2. W tle co 5 sekund na każdym urządzeniu uruchamia się proces pobierania danych, aby utrzymać lokalną kopię aktualną. Przedwcześnie pobiera on kontekst, dzięki czemu wznowienie sesji Claude nie powoduje opóźnień sieciowych przy każdym naciśnięciu klawisza.
  • UUID sesji są powiązane z katalogiem roboczym. Gdy Claude uruchamia się w określonym miejscu, hook session-start.sh przegląda lokalne pliki .meta w poszukiwaniu wpisu związанego z tym katalogiem roboczym i pobiera jego UUID. W ten sposób jeden UUID towarzyszy operatorowi przy przenoszeniu się między maszynami.
  • Hook synchronizacyjny, user-prompt-submit.sh, uruchamia się po każdym pytaniu zadanym przez Claude i wykonywa trzy operacje w ścisłej kolejności, zserializowane za pomocą flock:

    1. Pobranie i sformatowanie. Wykonanie polecenia git fetch origin main && git reset --hard powoduje, że lokalny plik active.jsonl zawiera transkrypcję z innej maszyny.
    2. Wyciągnij delta przed nadpisywaniem. Program odczytuje znacznik wodny, który wskazuje, ile linii zostało już wstawionych z drugiego komputera, wybiera tylko nowsze linie i zapisuje je do pliku tymczasowego. Musi to nastąpić przed krokiem 3. Wcześniejszy błąd spowodował zamianę tych dwóch kroków, przez co proces wstawiania nadal odczytywał własny zapis komputera.
    3. Wgraj swój własny zapis. Program kopiuje lokalny zapis sesji Claude do pliku active.jsonl, dokonuje komitowania i wgrania, a następnie ustawia znacznik wodny na liczbę linii w lokalnym zapisie.

    Gdy grupa komputerów się rozdzieli, delta zapisana w kroku 2 dociera do Claude jako wiadomość systemowa informująca, że drugi komputer dodał N wiadomości od ostatniego synchronizowania. Następna prośba użytkownika obejmuje wtedy kontekst obu komputerów.

    Odrębny plik .linecount przechowuje rzeczywistą łączną liczbę wierszy, ponieważ wc -l daje błędne wyniki, gdy transkrypcja została skrócona w celu umieszczenia jej w limicie. Aby uniknąć przekroczenia limitu 25 MiB w git, transkrypcje są przycinane do 1000 wierszy przed wysłaniem. Bez pliku .linecount każde skrócenie resetowałoby informację o długości tekstu, co powodowałoby ponowne dostarczenie tysięcy już widzianych wierszy.

    Operacje git zserializowane za pomocą flock zapobiegają uszkodzeniu pliku index.lock spowodowanemu jednoczesnymi wysyłkami danych, co zdarzyło się raz, gdy rozpoczęcie zablokowanej sesji pokryło się z uruchomieniem narzędzia do wzbogacania danych. Mechanizm ograniczający częstotliwość operacji, który można skonfigurować i którego domyślna wartość to 30 sekund, pomija pełną synchronizację przy szybkim wpisywaniu tekstu, dzięki czemu nie występuje opóźnienie w git związane z każdą literą.

    Koncepcją, którą warto przejąć, jest wykorzystanie Git jako magazynu stanu. Git pełni tu rolę czegoś więcej niż tylko repozytorium kodu – funkcjonuje jako trwały rejestr stanu agenta w stylu CRDT, przy czym dwa komputery operatora pełnią rolę odczytujących ostatecznie spójnych z tą informacją urządzeń.

    Konsolidacja pamięci jest ściśle powiązana z ciągłością między maszynami. Krok memory_consolidate, który przepisuje transkrypcje do czystych plików TASK.md i ANALYSIS.md, nie jest uruchamiany według zegara. Odrębna usługa consolidate_trigger sprawdza każdy etap rozmowy pod kątem czterech sygnałów: SHA commitu lub czasownika wskazującego na decyzję w transkrypcji, właśnie zamkniętego tematu recenzji, przestrzeni roboczej przypadku zawierającej ponad 5KB zgromadzonych transkrypcji lub co najmniej godziny od ostatniej konsolidacji. Każdy z tych sygnałów może ją uruchomić. Założeniem jest to, że informacje przestają się gromadzić po podjęciu decyzji, a nie po upływie określonego czasu, a łącznie te cztery sygnały pozwalają ocenić, czy rozmowa osiągnęła naturalną przerwę, dzięki czemu operator nie musi tego specjalnie podawać.

    Worktrees to coś więcej niż tylko szczegóły implementacyjne

    Wcześniej mówiono, że wszystko dzieje się w worktree’ach. Warto to rozwinąć, ponieważ wybór tego podejścia ma rzeczywiste znaczenie.

    Pierwotny projekt zakładał, że agent wygeneruje różnicę, a narzędzie harness uruchomi git apply w głównym repozytorium. To prowadziło do dwóch problemów, które ciągle się powtarzały:

    1. Kruchość nagłówków różnic. Claude generował różnice, w których numery linii były poprawne w momencie tworzenia, ale błędne po zatwierdzeniu innego komitów, przez co git apply odrzucał całą poprawkę.
    2. Zawieszony główny drzewo. Jeśli git apply --check zawiodło w połowie procesu, główny repozytorium pozostawał częściowo zastosowany, a następny proces uruchamiany w tym samym repozytorium odziedziczał to nieprawidłowe stan.

    worktree_manager to rozwiązanie tego problemu. Przed każdym agentem, który musi zmienić kod, tworzy on zabezpieczoną strukturę roboczą w katalogu ~/.worktrees/case-<case_id>-<stamp>/, pobraną z czystej gałęzi. Claude pracuje tam za pomocą swoich wbudowanych narzędzi edycji i zapisu, co eliminuje konieczność użycia git apply oraz procesu porównywania tekstów. Działa w ten sam sposób co ludzki inżynier: tworzy nową gałąź, pobiera jej kopię, a następnie edytuje, zapisuje i wysyła zmiany.

    Gdy prośba o integrację zostanie połączona lub zamknięta, struktura robocza jest usuwana. Częściowy błąd wpływa wyłącznie na tę strukturę, nie dotykając głównej kopii ani innych bieżących przypadków.

    To właśnie sprawia, że agenci działający równolegle mogą bezpiecznie funkcjonować na jednym komputerze. Bez osobnych struktur roboczych dla każdego przypadku dwa przypadki w tym samym repozytorium mogłyby konkurować o dostęp do stanu systemu plików. Dzięki tym strukturom przypadki są od siebie izolowane.

    Harness sam naprawia swoje prośby o integrację

    Zródło tego narzędzia znajduje się w prywatnym repozytorium na GitHubie. Gdy proces raportowania luk, finalizacji oraz przetwarzania zgłoszeń MR doprowadzi do stworzenia poprawki ulepszającej samo narzędzie, ta poprawka przechodzi przez własny proces CI obejmujący pytest, narzędzia do sprawdzania stylu kodu oraz weryfikację składni Pythona. Odrębna usługa pr-ci-fixer zajmuje się wszelkimi błędami w tym procesie w taki sam sposób, w jaki mr-monitor radzi sobie z błędami w zgłoszeniach typu MR.

    W rezultacie kod napisany do realizacji zadań oraz kod dotyczący samego narzędzia potrafi się sam naprawiać. Załóżmy, że jakaś poprawka dodaje nowy budżet na ponawianie prób i powoduje awarię istniejącego testu, co skutkuje niepowodzeniem procesu CI. pr-ci-fixer to wykrywa, uruchamia sesję Claude w odpowiednim worktree’u, naprawia problem i ponownie wysyła zmiany, dzięki czemu recenzent znajduje gotową do połączenia poprawkę o statusie „zielonym”. Ten cykl już niejednokrotnie pomógł w naprawianiu rzeczywistych zmian w narzędziu.

    Wskazanie pętli na sam źródło harnessa to najlepsza metoda sprawdzenia, czy zasługuje on na miano produktu seryjnego. Agent, który potrafi utrzymać swoje własne pull requesty w stanie „green”, jest prawdopodobnie godny zaufania przy obsłudze pull requestów innych osób.

    Pięć poziomów testów dla pięciu klas awarii

    Harness zawiera 4 656 funkcji testowych w 126 plikach. Ta liczba niewiele znaczy bez ramy określającej, co każdy test wykrywa, ponieważ testy nie są równe między sobą.

    Rama od L0 do L4 przypisuje każdy test do jednego z pięciu poziomów w zależności od tego, co izoluje:

    • L0 – czysta granica. Nic nie jest symulowane, brakuje systemu plików ani operacji wejścia/wyjścia. Obejmuje ona czystą logikę, taką jak funkcje bramkowe, wydobywanie danych w formacie JSON oraz obliczenia związane z progiem pewności, a to, co przeszło test na poziomie L0, będzie działać we wszystkich środowiskach implementacji.
  • L1, izolacja plików. Wykorzystuje rzeczywisty system plików tmpdir oraz prawdziwe wątki, przy czym biblioteka pathlib nie jest symulowana. Tutaj pojawiają się problemy z konkurencją między równoczesnymi piszącymi a błędy związane z atomowością plików stanu, których L0 zgodnie z projektem nie może wykryć.
  • L2, izolacja usług. Rzeczywiste podprocesy działają z wykorzystaniem symulowanych zewnętrznych API (Slack, GitLab, Claude). Błędy w sposobie współpracy komponentów, w tym w routingu zadań, schematach danych oraz przejściach pomiędzy stanami plików stanu, ujawniają się na tym poziomie, ponieważ L1 izoluje jedynie zachowanie systemu plików.
  • L3, sandbox. Poprzez podproces tworzone jest rzeczywiste repozytorium git, natomiast odpowiedzi od Slack i Claude są fałszywe. Całe sekwencje procesów przeprowadzane są przy użyciu rzeczywistego modelu obiektów git, takiego jak rejestracja zadań, rozpoczęcie fazy implementacji oraz oczekiwanie na potwierdzenie.
  • L4, macierz integracji. Jej sześć podkategorii to umowy aktorów (L4a), macierz administracji według ról (L4b), spójność stanu w różnych uruchomieniach (L4c), łańcuchy między usługami (L4d), role zewnętrznych aktorów (L4e), luki w metodologii (L4f) oraz sekwencjonowanie czasowe (L4h i L4i).
  • L4g, odtwarzanie w środowisku produkcyjnym. Trzynaście testów odtwarza konkretnych incydentów z środowiska produkcyjnego jako testy regresji. Są one pisane po pojawieniu się błędu w produkcji, zapisując dokładnego aktora, kontekst i stan, które go wywołały, i są przechowywane trwałe, aby nie mógł on powrócić.
  • Testy L4g wymagają najwięcej czasu na napisanie, ale są najcenniejsze do utrzymania. Zawierają one informacje z pamięci instytucjonalnej na temat sposobów awarii, które pozostają niewidoczne aż do ich ponownego wystąpienia.

    Przewodnik stylu zapobiegający temu, by tekst wyglądał jak napisany przez maszynę

    Każda proponowana zmiana przechodzi przez kolejny filtr przed podaniem, a procedura SOP została stworzona specjalnie po to, by wynik nie wyglądał na wygenerowany przez AI. Zasady są jednoznaczne:

    • W ogóle nie wolno podawać informacji o AI. Brak tagu Co-Authored-By: Claude, brak napisu „Generated with Claude Code” oraz brak sufiksu [bot] w komunikatach o komitach. Proponowana zmiana należy do zespołu, a nie do modelu.
    • Zabronione są nagłówki wymagające uzupełnienia danych. Zakazane są szablony typu „Co robi ta proponowana zmiana / dlaczego jej potrzebujemy”. Opis musi być ciągłym tekstem z konkretną listą punktową Changes:, napisanym w sposób, w jaki inżynier napisałby go ręcznie.
    • Zabronione są tagi pogrubiające wewnątrz tekstu. Nie dopuszcza się etykiety Root cause: z następującym po niej akapitem, podobnie jak tagi Fix: i Risk:. Pogrubienie w stylu tagów to najbardziej wiarygodny znak wygenerowania tekstu przez maszynę.
  • Dowód pracy musi być możliwy do odtworzenia za pomocą polecenia. „Sprawdzone lokalnie” nie liczy się. pytest tests/test_foo.py::test_bar -v z rzeczywistym poleceniem oraz odpowiednimi 3 liniami wyników tak, natomiast tak.
  • Gdy tworzony jest opis zmiany, zasady SOP są wstawiane do promptu służącego do generowania poprawek. Znajdują się one oddzielnie od szablonu promptu, ponieważ są edytowane przez operatorów: można je modyfikować w dowolnym momencie, a następna zmiana będzie przestrzegać nowych zasad bez konieczności restartu demona czy zmiany kodu. Nawet pętla gap loop może proponować edycje zasad SOP, więc meta-zasady dotyczące sposobu tworzenia zmian przez agenta są przekazywane w formie samych zmian, przeciwstawionych plikowi, który je zawiera.

    To ma większe znaczenie, niż się wydaje. Nic tak szybko nie niszczy zaufania do zautomatyzowanego procesu MR jak sytuacja, w której zespół zauważa, że wszystkie opisy wyglądają jakby zostały stworzone przez maszynę. Gdy tylko takie wrażenie się utrwali, poziom trudności fuzji rośnie, autentyczne sygnały od operatora giną, a ci zaczynają chcieć ręcznie przepisywać opisy, co podważa cel całego procesu. Standardy stylowe zapewniają, że opisy będą pasować do tych napisanych ręcznie, co utrzymuje ten cykl w ruchu.

    Jeden ticket, jedna MR – od początku do końca

    Oto, co dzieje się, gdy przychodzi prawdziwy ticket. Podane czasy są orientacyjne.

    W całym procesie ten cykl zazwyczaj trwa od 30 do 90 minut w czasie rzeczywistym dla małego lub średniego ticketa. Łączny czas aktywnego zaangażowania operatora wynosi zwykle mniej niż 5 minut: potwierdzenie analizy, opcjonalne kierowanie niejednoznacznymi decyzjami oraz opcjonalne kliknięcie „fuzja”.

    Narastają badania. Po przetworzeniu 30 przypadków powstaje 30 plików gap_report*.md, z których każdy odnotowuje coś, co według LLM mogłoby być lepsze. Komenda admin: gap-patterns wykonywana co dwa tygodnie grupuje je według powtarzających się wzorców błędów, a kolejna faza finalizacji przekształca największe grupy w propozycje zmian skierowane do repozytorium harness, co oznacza, że harness sam się ulepsza. To jest ucieleśniony cykl samodoskonalenia.

    Jak porównuje się z innymi harnessami

    Kilka innych systemów ma podobną strukturę do harnessa. Tabela pokazuje, w czym ten system się z nimi pokrywa, a w czym różni:

    Miejsca, gdzie ten harness zagłębia się niezwykle głęboko:

    • Zamknięcie hipotez przed podjęciem decyzji. Claude formułuje co najmniej sześć hipotez kandydujących, zanim zostanie uruchomiona choćby jedna iteracja dochodzenia, co zapobiega racjonalizacji po fakcie. Żaden z porównywanych narzędzi komercyjnych nie oferuje równoważnego rozwiązania.
    • Ramka kontrolna jakości składająca się z 19 funkcji z mechanicznymi górnymi granicami pewności. Pewność jest ograniczana stałą formułą, która zaczyna się od wartości 1,0 i zmniejsza się o 0,08 za każde nierozwiązane pytanie oraz o 0,05 za każdy nie sprawdzony źródło, więc jest to coś więcej niż samodzielnie podana wartość. Dodatkowo obowiązują kary za problemy w poszczególnych wymiarach, a całość składa się z trzech grup kontrolnych obejmujących spójność logiczną, kompletność strukturalną oraz górne granice twierdzeń. Żaden z porównywanych narzędzi komercyjnych nie posiada takiego rozwiązania.
  • Samodoskonalenie polegające na modyfikacji własnej bazy kodu narzędzia. Raporty dotyczące luk stają się filtrowanymi propozycjami zmian skierowanymi przeciwko własnemu kodowi narzędzia, jego interfejsom oraz procedurom operacyjnym. Najbliższym odpowiednikiem w środowisku akademickim jest SICA (arXiv 2504), który podaje wyniki testów, ale pozostaje badaniem offline, a żaden system produkcyjny nie wdraża autonomicznych zmian w swoim kodzie narzędziowym.
  • Dostosowywanie procedur operacyjnych do specyfiki projektu. Procedury operacyjne każdego projektu rozwijają się na podstawie danych dotyczących jego własnych przypadków awarii, a w ciągu 20 do 30 przypadków odzwierciedlają one powtarzające się błędy w badaniach w danym obszarze. Porównanie nie wykazało żadnego innego narzędzia produkcyjnego, które robi to samo.
  • Orkiestracja wielu agentów z wyraźnym przekazywaniem zadań. Łańcuch działa od oncall_run do case_followup, gap_analyzer, finalize_case oraz dev-agent. Każde połączenie ma zdefiniowany schemat przekazywania zadań oraz oddzielny drzewo prac, dzięki czemu praca jest rozdzielana między agentami zamiast być przekładana na jednego.
  • Synchronizacja stanu między maszynami za pomocą wstrzykiwania znaczników wodnych opartych na git. Jedna sesja Claude może być kontynuowana na laptopie lub zdalnej maszynie wirtualnej z całkowicie nienaruszonym kontekstem, a nikt nie musi ręcznie nic synchronizować.
  • Wbudowane pętle automatycznego naprawiania w CI oraz automatycznych odpowiedzi od recenzentów. Devin, produkt komercyjny, jest jedynym, który posiada takie wbudowane mechanizmy samonaprawcze, podczas gdy wszystkie inne frameworki wymagają dostosowania.
  • Miejsca, w których jest celowo uproszczone:

    • Brak formalnego narzędzia do oceny. LLM nie jest oceniany na podstawie żadnych standardów, a informacje zwrotne z produkcji służą jako kryterium oceny.
    • Brak panelu sterowania – dostępne są tylko Slack i logi.
    • Brak wskaźnika kosztów. Użycie tokenów jest monitorowane w sposób luźny i nie jest wyświetlane na panelu sterowania; koszty są ograniczane raczej krokami potwierdzenia przeprowadzanymi przez człowieka.

    Co pozostało oraz meta-pętla

    Narzędzie wciąż się rozwija – istnieją jeszcze nierozwiązane kwestie dotyczące pełnej obsługi pętli autonomicznej, pomiaru kosztów oraz formalnego narzędzia do oceny, ale żadna z nich nie stanowi przeszkody. Już teraz realizuje swoje główne zadanie: przekształca sygnały z Slacka w połączony kod, odpowiada na uwagi recenzentów w ciągu nocy, samodzielnie naprawia błędy w procesie CI oraz wprowadza własne ulepszenia do następnej wersji.

    Prawdziwą lekcją jest meta-pętla. Każda blizna w tym systemie istnieje dlatego, że rzeczywisty problem wywarł na niej presję. Budowa i działanie to jedna pętla poruszająca się z dwiema różnymi prędkościami. Sygnał z produkcji wraca do źródła w tym systemie, agent opracowuje rozwiązanie, człowiek je sprawdza i łączy z istniejącym kodem, a kolejna kategoria błędów jest coraz bliżej usunięcia.

    System nie jest jeszcze ukończony. System produkcyjny, który sądzi, że jest gotowy, przestał gromadzić „blizny”, co oznacza po prostu, że nie jest już uruchamiany pod rzeczywistym obciążeniem.

    Najpierw należy stworzyć pętlę, a dopiero potem uczynić ją autonomiczną. System autonomiczny bez ścieżki zwrotnej do swojego własnego substratu jedynie szybciej i w większej liczbie przekazuje te same błędy, dlatego kolejność ma znaczenie.

    Literatura pokrewna

  • Notatki praktyczne: Jak przekształciłem moją galerię zdjęć w autonomicznego agenta AI — Szczegółowy przewodnik po Notatkach praktycznych: Jak przekształciłem moją galerię zdjęć w autonomicznego agenta AI: umowy, sprawdzenia oraz gotowe fragmenty kodu dla zespołów wdrażających ten model.
  • Tworzenie narzędzia DeepAgents Harness: uprawnienia, podagenty i zatwierdzenie przez człowieka — Praktyczne tworzenie planera projektu systemowego z wykorzystaniem DeepAgents: serwery plików, reguły uprawnień, podagenty, mechanizmy zatwierdzenia, umiejętności i MCP, a także miejsca, gdzie ocena modelu nadal zawodzi.
  • Używanie Codex w VS Code: zakres działania, punkty odzyskiwania oraz rozwiązania typowych problemów — Praktyczny przewodnik po rozszerzeniu Codex dla VS Code: instalacja, logowanie, pierwsze zadań dla agenta, różnice między chmurą a interfejsem wiersza poleceń, współdzielone ustawienia oraz rozwiązania problemów z WSL, procedury zatwierdzania i autoryzacji zdalnej.
  • Pięć warstw w Claude Code: pamięć, umiejętności, hooki, podagenty i wtyczki — Claude Code jako środowisko działania agenta: pamięć CLAUDE.md, umiejętności dostępne na żądanie, hooki deterministyczne, izolowane podagenty oraz wtyczki, które łączą to wszystko dla zespołu.
  • Odbudowa agenta w stylu Claude Code za pomocą LangChain Deep Agents — Jak działa mechanizm sterowania agentem programistycznym, od podstawowego cyklu działania po narzędzia, planowanie, pamięć, podagenty oraz zatwierdzenia w izolowanym środowisku, pokazane krok po kroku przy użyciu deepagents.
  • Pięć warstw mechanizmu sterowania agentem, wraz z kodem, który bez nich nie działa — Praktyczny przegląd pięciu miejsc, w których mechanizm sterowania agentem wprowadza zasady do kodu, od granic wykonywania po łańcuchy kontekstowe, oraz tego, co badania mogą, a czego nie mogą udowodnić.
  • Praktyczne notatki: Claude Code Agent Teams: Praktyczny przewodnik po budowaniu zespołu — Szczegółowy opis Praktycznych notatek: Claude Code Agent Teams: Praktyczny przewodnik po budowaniu zespołu, w tym informacje o kontraktach, sprawdzaniach oraz miejscach na kod dostępnych dla zespołów wdrażających ten model.
  • Przejście na Claude Opus 5.5: Pięć istotnych zmian związanych z identyfikatorem modelu — Wskazówki dotyczące przejścia na Claude Opus 5.5: ciągłe myślenie, wymuszony wybór narzędzia, blokady myślenia związane z modelem, narzędzia do korzystania z komputera oraz cichy przepływ postępów.